杭電15年筆試真題詳解
目錄
題目一:
給定一個字串,計算字串中數值的個數並求和。其中還包含了負號,若緊跟負號的是一個數值,則表示這是一個負數,若後面跟著的不是數字,則不表示什麼。輸入:一個字串輸出:數值個數數值和列子
輸入:312ab-2—9–a
輸出:3 301
多年不寫程式碼,現在寫個這麼簡單的判斷都如此笨拙,而且程式碼醜陋無比,後來的考杭電的大佬,如果有更好的思路,麻煩在下面貼出你的程式碼,以便大家學習
思路:
1.對這個字串進行暴力遍歷。
2.如果遍歷過程中遇到“-”號,並且”-“號後面就是數字的話,那麼就進入一個死迴圈,並且更新暴力指標,直到把這個負數完整的取出來,解除死迴圈,繼續暴力該字串。
3.如果遇到數字的話,那麼就可以肯定他一定是正數,負數的話一定會走第二步,走不到這一步的。進入死迴圈,更新暴力指標,把這個字串儲存到一個臨時陣列中,然後使用atoi函式,將這個字串轉換成數字即可。
4.我們把2,3兩步中取出來的字串放入到一個sum陣列當中。
5.把sum陣列中取出來的所有正數或者負數,進行累加求和。我們就可以得到最終的結果。
方法一:
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
int main(){
string str;
cin>>str;
int sum[100],sum_cnt=0;
char tmp[20];//中間變數,用來擷取數字
for(int i=0;i<str.length();i++){//從頭到尾開始進行遍歷
//memset(tmp,0,sizeof(tmp));//初始化字串
int tmp_cnt=0;
if(str[i]<='9'&&str[i]>='0'){
while(true){//處理正數
if(str[i]<='9'&&str[i]>='0'){
//如果是數字,開始把這個數儲存到數組裡,然後走到不是數字為止。進行字元轉換
tmp[tmp_cnt++]=str [i];
i++;
}
else{
tmp[tmp_cnt]=0;//輸入字串結束符。
tmp_cnt=0;//重新初始化,準備下次使用
sum[sum_cnt++]=atoi(tmp);
break;
}
}
}
if(str[i]=='-'&&str[i+1]<='9'&&str[i+1]>='0'){
while(true){
i++;//開始從數字位置開始暴力。
if(str[i]<='9'&&str[i]>='0'){
//如果當前位置是負號,並且下一位是數字,將數字儲存到一起
tmp[tmp_cnt++]=str[i];
}
else{
tmp[tmp_cnt]=0;//輸入字串結束符。
sum[sum_cnt++]=-atoi(tmp);//將取出的數值置為負數
break;
}
}
}
}
int ans=0;
for(int i=0;i<sum_cnt;i++)
ans+=sum[i];
cout<<sum_cnt<<" "<<ans<<endl;
return 0;
}
0325日重新修改以上程式碼,套用函式將其中的取數操作變得簡單化一些
方法二:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<fstream>
#define MAX 1010
using namespace std;
char a[1010];
char temp[1010];//該陣列用來儲存分隔開的整數。
int sum[1010],sum_cnt,i;
void qushu(){
int cnt=0;//記錄數字長度
if(a[i]=='-'){//先對符號進行處理,後面就可以進行統一化。
temp[cnt++]='-';
i++;
}
for(;i<strlen(a);i++){//
if(a[i]>='0'&&a[i]<='9')
temp[cnt++]=a[i];
else
break;
}
temp[cnt]=0;//每取出一次數,就把陣列最後一位置為字串結束標誌
sum[sum_cnt++]=atoi(temp);
i--; //跳出函式之後還是for迴圈,這可能就會導致直接跳兩個字元,所以縮減一下
}
int main(){
cin>>a;
sum_cnt=0;//記錄有多少個整數
for(i=0;i<strlen(a);i++){
if(a[i]=='-'&&a[i+1]>='0'&&a[i+1]<='9')//如果該數是負數
qushu();
else if(a[i]>='0'&&a[i]<='9')//如果該數是正數
qushu();
//這兩個條件判斷應該是非此即彼的關係。所以我們最好用else if進行連線
}
int ans=0;
for(int i=0;i<sum_cnt;i++)
ans+=sum[i];
cout<<sum_cnt<<" "<<ans<<endl;
return 0;
}
方法三:
來自半墨生煙的寫法
利用i=i+j來更新指標,我覺得還蠻巧妙的。
#include<stdio.h>
#include<string.h>
int main()
{
int i,j,sum=0,temp=0,len,t=0,flag=1;
char a[1000];
gets(a);
len=strlen(a);
for(i=0;i<len;i++)
{
if(a[i]=='-'&&a[i+1]>='0'&&a[i+1]<='9')//如果為負數
{
temp=0;
for(j=1;a[i+j]>='0'&&a[i+j]<='9';j++)
temp=temp*10+a[i+j]-'0';//臨時存一個數
i=i+j;
sum-=temp;
t++;
}
else if(a[i]>='0'&&a[i]<='9')//如果為正數
{
temp=0;
for(j=0;a[i+j]>='0'&&a[i+j]<='9';j++)
temp=temp*10+a[i+j]-'0';
i=i+j;
sum=sum+temp;
t++;
}
}
printf("%d %d\n",t,sum);
return 0;
}
給定一個數字矩陣,如果上下左右數值相同,則表示是一個連通的區域。求矩陣中連通塊的數量。輸入:先是矩陣的行數和列數接著是矩陣輸出:連通塊的數量
例子
5 6
4 4 4 4 4 4
4 2 3 3 1 4
4 2 2 3 1 4
4 2 3 3 1 4
4 4 4 4 4 4
輸出
4
說明所有4能連起來,還有2 3 1,都有各自能連通塊。
思路:
這道題有很明顯的BFS傾向,然後我就用BFS做了。。。
1、設定一個判斷模組兒,(BFS最噁心的一個地方就是,它亂走,很有可能就會越界,或者直接走你走過的地方。)所以你需要設定一個判斷模組兒,看自己是否走出地圖的範圍了,或者看一下你是不是走的是之前已經走過的路。而且這道題有個特點就是,只有兩個字元相同了,你才能到達,他們之間才有路。
2、寬搜模組兒,實現一個點到它上下左右四個位置的遍歷,對每一個位置進行判斷。若判斷模組兒為真,就加入佇列,若為假,就說明該位置不可用。
寬搜流程:
- 先把第一個點放入佇列(這個點是我們寬搜的起點),並標記為訪問過了。
- 進入一個迴圈,條件為佇列不空。這個是告訴我們只要你還有走路的可能,你就要一直走下去。
- 每次迴圈取出隊頭,然後出隊,標記為訪問過。
- 對取出的隊頭進行上下左右四個位置的嘗試,每次嘗試都判斷一下是否能夠滿足判斷模組兒,滿足就入隊,不滿足就continue
3、我們要判斷的是連通性。那我們要知道一點,需要一次BFS就能訪問到所有節點的圖一定是一個連通。需要兩次才能訪問完的一定是兩個連通。我們利用這個性質寫一個判斷。如果BFS後還有沒被訪問過的點,那麼連通的數量就+1.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<iostream>
#define MAX 150
using namespace std;
struct N{
int x,y;
}now,tmp;
queue<N>q;
int map[MAX][MAX],n,m;
int X[4] = {0,0,1,-1};
int Y[4] = {1,-1,0,0};
bool visit[MAX][MAX];
bool judge(int x,int y,int value){//判斷下一層的點是否符合規範
if(x>n||y>m||x<0||y<0)//越界不合法
return false;
else if(visit[x][y])//訪問過不合法
return false;
else if(map[x][y]==value)//儲存的值相同代表術語同一個區域
return true;
else
return false;
}
void BFS(int x,int y,int value){
int newx, newy;
tmp.x=x;tmp.y=y;
q.push(tmp);
visit[tmp.x][tmp.y]=true;//標記為已經訪問過
while(!q.empty()){
now =q.front();
q.pop();//從隊頭中取出元素。
for(int i=0;i<4;i++){
newx = now.x + X[i];
newy = now.y + Y[i];
if(judge(newx,newy,map[now.x][now.y])){
//進行比對,看是否是連通的。
//傳給judge的是now節點的值,我們要得到的是now節點和temp節點比對之後的結果。
tmp.x=newx;tmp.y=newy;
q.push(tmp);
visit[tmp.x][tmp.y]=true;
}
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)//開始建立圖
for(int j=0;j<m;j++)
cin>>map[i][j];
int ans=0;
memset(visit,false,sizeof(visit));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
if(!visit[i][j]){//如果該節點還沒有被訪問過,就說明是一個新的連通區
ans++;
BFS(i,j,map[i][j]);
}
}
cout<<ans<<endl;
}
寫法二:
思路是一樣的,不過中間有一點點的寫法上的不同。Update 0325
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<fstream>
#include<queue>
#define MAX 1010
using namespace std;
int X[4]={0,0,1,-1},Y[4]={1,-1,0,0};
char map[MAX][MAX];
int n,m;
bool visit[MAX][MAX];
struct loc{
int x;
int y;
char value;
}now,temp;
bool judge(int x,int y,char value){//value標記現在表示通路的字元
if(x>=n||y>=m||x<0||y<0)//越界
return false;
if(visit[x][y])//如果已經訪問過了
return false;
if(map[x][y]==value)//如果該位置的值也是通路上的值
return true;
return false;
}
void BFS(int x,int y,char value){
now.x=x;now.y=y;
now.value=value;
queue<loc>q;
q.push(now);
visit[x][y]=true;
while(!q.empty()){
now=q.front();
q.pop();
for(int i=0;i<4;i++){
temp.x=now.x+X[i];
temp.y=now.y+Y[i];
temp.value=map[temp.x][temp.y];
if(judge(temp.x,temp.y,now.value)){
q.push(temp);
visit[temp.x][temp.y]=true;
}
}
}
}
int main(){
int ans=0;
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>map[i][j];
memset(visit,false,sizeof(visit));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(!visit[i][j]){//如果還沒被訪問過,就說明肯定還有連通塊兒
ans++;
BFS(i,j,map[i][j]);
}
cout<<ans<<endl;
return 0;
}
做法三:DFS
感覺最近幾年的試題明顯有很強的深搜傾向,大部分題都是和深搜相關的
程式碼如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define N 101
using namespace std;
bool visit[N][N];//標記是否訪問過該節點
int DFS_COUNT;//記錄有多少個連通分量
int X[4]={0,0,1,-1},Y[4]={1,-1,0,00};//座標轉移陣列
int n,m;//輸入圖的大小
char map[N][N];//用來儲存圖
bool judge(int x,int y,char value){
if(x>=n||x<0||y>=m||y<0)//如果越界就返回false
return false;
if(visit[x][y])//如果訪問過
return false;
if(map[x][y]==value)//如果字元相同才表示這一條是通路
return true;
return false;
}
void dfs(int x,int y){
visit[x][y]=true;
for(int i=0;i<4;i++ ){//進行圖的上下左右的遍歷
int newx=x+X[i];
int newy=y+Y[i];
if(judge(newx,newy,map[x][y]))//如果該位置滿足條件就遞迴
dfs(newx,newy);
}
}
void DFS(){
memset(visit,false,sizeof(visit));//初始化訪問陣列
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)//對整個圖進行遍歷
if(!visit[i][j]){
DFS_COUNT++;
dfs(i,j);
}
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)//儲存圖
for(int j=0;j<m;j++)
cin>>map[i][j];
DFS();//進行深搜
cout<<DFS_COUNT<<endl;//輸出連通個數
return 0;
}
這道題如果稍微擴充套件下的話,可以和今年的初試題目一樣,讓你去判斷環,判環的話,其實還是蠻難的,至今沒有什麼好的思路,對於上面這道題,我們能看到一個迴路,就那個全是“4”的圈兒,哪個大佬說一下怎麼判斷出有這個環