當N較小時考慮搜索。
DFS大致模板
//會有相對應的方向變量 例如: dx[4].dy[4]; void DFS(int x,int y) { if(滿足所需要的條件) { 相應的操作;return;} else{ for(int i= ; ;) //如果是方向的話,會枚舉方向 { 枚舉加方向的新坐標; if(界限 :例如:不能出到地圖外,有障礙,已經訪問過) continue; 設置已經訪問新坐標; DFS(新坐標); 恢復到未被訪問; } } } int main() { // 需注意要將起點設置成已訪問。 }
例題:
P1605 迷宮:
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int n,m,t; 5 int StrX,StrY; 6 int EndX,EndY; 7 int HndX,HndY; 8 int dx[4]= {1,-1,0,0}; 9 int dy[4]= {0,0,-1,1}; 10 int vis[30][30]; 11 int ans; 12 void dfs( int x, int y) //深搜 13 { 14 if(x==EndX &&y==EndY) //一定要把坐標打對!!!!! 15 { 16 ans++; 17 return ; 18 } 19 else 20 { 21 for(int i=0; i<4; i++) 22 { 23 int NewX=x+dx[i],NewY=y+dy[i]; 24 //在這道題中,沒有0,0結點,所以NewX<1 NewY<1輸出才正確 25 if(NewX>n || NewY>m ||NewX<1 ||NewY<1 || vis[NewX][NewY]==1 ||vis[NewX][NewY]==2) continue; 26 else 27 { 28 vis[NewX][NewY]=1; 29 dfs(NewX,NewY); 30 vis[NewX][NewY]=0; 31 } 32 } 33 } 34 35 } 36 int main() 37 { 38 scanf("%d%d%d",&n,&m,&t); 39 scanf("%d%d%d%d",&StrX,&StrY,&EndX,&EndY); 40 while(t--) 41 { 42 scanf("%d %d",&HndX,&HndY); 43 vis[HndX][HndY]=2; //將障礙設置成2, 44 } 45 vis[StrX][StrY]=1;//將起點1設置成已經訪問過的狀態!!!!切記 46 dfs(StrX,StrY); 47 printf("%d",ans); 48 49 return 0; 50 }
------------------------------------------------------------------------------------------------------------------------------------------------------
P1149:火柴棒等式
在這道題中,運用了回溯的思想:
過程是:先將所有的數所需要的火柴數遍歷計算出來。然后在Search函數中:根據n-number[i]>=0判斷是否還能減去當前火柴數。
如果可以,就將這個數的放入b[]中,載判斷是否已經A,B,C,三個數都已經給你找齊,如果找齊的話,再判斷是否相等,並且n是否恰好用完。
如果l還不等於3的話,在繼續尋找下一個數。
最后保存其初始狀態。(比較重要)
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 //使用回溯的方法 7 int n; 8 int ans; 9 int b[4];//存找到的三個數 10 int number[1112]= {6,2,5,5,4,5,6,3,7,6}; 11 void Search(int l) 12 { 13 for(int i=0; i<1112; i++) 14 { 15 16 if(n-number[i]>=0) 17 { 18 b[l]=i; 19 n=n-number[i]; 20 if(l==3) //如果符合火柴數的三個數都求出來了,然后判斷 21 {if(b[1]+b[2]==b[3] && n==0) ans++;} 22 else Search(l+1); 23 n=n+number[i]; //不管l是不是等於3,都要保存之前的狀態 24 //在這里要特別注意,當判斷是否l==3,如果=3,判斷 25 //如果!=3,再繼續下一個數的搜尋。 26 } 27 } 28 } 29 int main() 30 { 31 scanf("%d",&n); 32 n=n-4; 33 for(int i=10; i<1112; i++) //注意這里是從10開始,不是從0開始。 34 { 35 number[i]=number[i/10]+number[i%10]; //在這里也需要注意,在后面三位數中,分割成一個兩位數,和一位數,因為兩位數,一位數已經算過了,所以就可以直接加起來 36 } 37 Search(1); 38 printf("%d",ans); 39 }
----------------------------------------------------------------------------
P1596 Lake Counting
:這道題是深搜模板題。判斷連通圖有多少。
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int n,m,ans; 5 const int MAXN=105; 6 char map[MAXN][MAXN]; 7 8 void dfs(int x,int y) 9 { 10 for(int i=-1; i<=1; i++) 11 //在這里一共需要8個方向,循環下來加原點一共9個方向 12 { 13 for(int j=-1; j<=1; j++) 14 { 15 int NewX=x+i,NewY=y+j; 16 if(NewX>=n ||NewY>=m ||NewX<0 || NewY<0 || map[NewX][NewY]=='.') 17 continue; //判斷新的坐標的錯,不要打錯!! 18 map[NewX][NewY]='.'; 19 dfs(NewX,NewY); 20 } 21 } 22 return; 23 } 24 int main() 25 { 26 scanf("%d%d",&n,&m); 27 for(int i=0; i<n; i++) 28 scanf("%s",map[i]); 29 for(int i=0; i<n; i++) 30 { 31 for(int j=0; j<m; j++) 32 { 33 if(map[i][j]=='W') 34 { 35 map[i][j]='.'; 36 ans++; 37 dfs(i,j); 38 } 39 } 40 } 41 printf("%d",ans); 42 return 0; 43 }
我出現的問題:
1.在判斷界限時手誤打錯NewX NewY 和x,y;
2. 因為我讀入的時候是從下標0開始的,所以在判斷界限時應該是NewX>=n 時就已經越界了;
3.之前一直寫成4個方向。在這里是8個方向。
------------------------------------------------------------------------------------------------------------------------------
P1219八皇后
采用DFS
#include <iostream> #include <cstdio> using namespace std; int n,a[14],l[27],r[27],ans[14],js=0; //標記是否相應位置上有皇后 void print() { for(int i=1;i<=n;i++) printf("%d ",ans[i]); cout<<endl; } void dfs(int i) { if(i>n) { js++; if(js<=3) print(); } for(int j=1;j<=n;j++) { if(a[j]==0 && l[i+j-1]==0 &&r[i-j+n]==0) /*判斷界限,當前位置,左對角線,右對角線的*/ { a[j]=1; l[i+j-1]=1; r[i-j+n]=1; ans[i]=j; dfs(i+1);
//恢復初始時的狀態 a[j]=0; l[i+j-1]=0; r[i-j+n]=0; } } } int main() { scanf("%d",&n); dfs(1); printf("%d",js); }
對於這道題有幾個重要的點:
1.數組大小,
2.判斷左對角線和右對角線的公式
3.將答案存儲在ans[]數組中
4.輸出答案;
------------------------------------------------------------
P1086 花生采摘,采用貪心的方法。
這道題是將所有的可采的花生排序,然后一直找可采的最大的花生。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 const int MAXN=20; 6 int map[MAXN][MAXN]; 7 int k,t,m,n,ans,u; 8 struct node 9 { 10 int x; 11 int y; 12 int time; 13 int num; 14 }Edge[400]; 15 bool cmp(node a,node b) 16 { 17 return a.num>b.num; 18 } 19 void dfs() 20 { 21 for(int i=0; i<k; i++) 22 { 23 u=Edge[i].x+1; //深度 24 if(i==0) Edge[i].time=Edge[i].x+1+1; //第一步的時間等於第一個點的深度+1;因為下標是從0開始,所以要再加1; 25 else Edge[i].time=Edge[i-1].time+abs(Edge[i].x-Edge[i-1].x) 26 +abs(Edge[i].y-Edge[i-1].y)+1; //如果不是第一步的,要算出兩個點之間的移動時間,以及采摘時間,以及之前的總時間 27 if(Edge[i].time+u<=t) {ans+=Edge[i].num;} //如果采摘花生的過程中花費的時間(包括采摘,走路)加回到原點的時間=約定的時間,則加入花生數。 28 } 29 } 30 int main() 31 { 32 scanf("%d%d%d",&m,&n,&t); 33 for(int i=0; i<m; i++) 34 { 35 for(int j=0; j<n; j++) 36 { 37 scanf("%d",&map[i][j]); 38 if(map[i][j]!=0) 39 { 40 Edge[k].num=map[i][j]; 41 Edge[k].x=i; 42 Edge[k].y=j; 43 k++; 44 } 45 } 46 } 47 sort(Edge,Edge+k,cmp); 48 dfs(); 49 printf("%d",ans); 50 return 0; 51 }
-----------------------------------------------------------------------------------
P1706 全排列問題
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int n; 5 int ans[10]; 6 int vis[10]; 7 void print() 8 { 9 for(int i=1;i<=n;i++) 10 { 11 printf("%5d",ans[i]); 12 } 13 printf("\n"); 14 } 15 void dfs(int x) 16 { 17 if(x==n) print(); 18 for(int i=1;i<=n;i++) 19 { 20 if(!vis[i]) 21 { 22 vis[i]=1; 23 ans[x+1]=i; 24 dfs(x+1); 25 vis[i]=0; 26 } 27 } 28 } 29 int main() 30 { 31 scanf("%d",&n); 32 dfs(0); 33 return 0; 34 }
---------------------------------------------------------------------------
AT1350 判斷是否可以到達指定的地點:不使用回溯,如果走過,直接標記。
#include <iostream> #include <cstdio> using namespace std; char map[510][510]; int n,m; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; int vis[510][510]; int StrX,StrY,EndX,EndY; bool dfs(int x,int y) { if(x==EndX &&y==EndY) return true; for(int i=0;i<4;i++) { int xx=x+dx[i],yy=y+dy[i]; if(map[xx][yy]=='#' || xx<0 ||yy<0 ||xx>=n || yy>=m ||vis[xx][yy]==1) continue; vis[xx][yy]=1; if(dfs(xx,yy)) return true; } return false; } int main() { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { scanf("%s",map[i]); } for(int i=0;i<n;i++){ for(int j=0;j<m;j++) { if(map[i][j]=='s') StrX=i,StrY=j; if(map[i][j]=='g') EndX=i,EndY=j; } } vis[StrX][StrY]=1; if(dfs(StrX,StrY)) cout<<"Yes"<<endl; else cout<<"No"<<endl; return 0; }
---------------------CF445A---- 棋子問題-------------------------
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int n,m; 5 char map[110][110]; 6 int dx[5]={-1,1,0,0,0}; 7 int dy[5]={0,0,-1,1,0}; 8 inline void dfs(int x,int y,int i) 9 { 10 int j; 11 if(x<0 ||x>n ||y<0 ||y>=m) return; 12 if(i==0){ map[x][y]='W'; /*cout<<map[x][y]<<endl;*/} 13 if(i==1) map[x][y]='B'; 14 // if(x==n && y==m) return; 15 for(j=1;j<=4;j++) 16 { 17 int xx=x+dx[j],yy=y+dy[j]; 18 //if(xx<1 || yy<1 || map[xx][yy]=='-' ||xx>n || yy>m ) continue; 19 if(map[xx][yy]=='.') 20 { 21 if(i==0) dfs(xx,yy,1); 22 if(i==1) dfs(xx,yy,0); 23 } 24 25 } 26 } 27 int main() 28 { 29 scanf("%d%d",&n,&m); 30 int strX,strY; 31 for(int i=1;i<=n;i++) 32 { 33 scanf("%s",&map[i]); 34 } 35 for(int i=1;i<=n;i++) 36 { 37 for(int j=0;j<m;j++) 38 { 39 if(map[i][j]=='.') 40 dfs(i,j,1); 41 } 42 } 43 for(int i=1;i<=n;i++) 44 printf("%s\n",map[i]); 45 return 0; 46 }
-------------------------------------------------------------------------------------------------
P1162顏色填塗
由數字000組成的方陣中,有一任意形狀閉合圈,閉合圈由數字111構成,圍圈時只走上下左右444個方向。現要求把閉合圈內的所有空間都填寫成222.例如:6×66 \times 66×6的方陣(n=6n=6n=6),塗色前和塗色后的方陣如下:
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 1
想法:先將輸入的所有的0都變成2,然后外圈再圍一圈2,這樣在1外面的都聯通了,再從(0,0)dfs,將外圈的2全部變成0就可以啦
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 const int MAXN=200; 5 int map[MAXN][MAXN]; 6 int n; 7 int vis[MAXN][MAXN]; 8 int dx[4]={0,0,-1,1}; 9 int dy[4]={-1,1,0,0}; 10 void dfs(int x,int y) 11 { 12 for(int i=0;i<4;i++) 13 { 14 int xx=x+dx[i],yy=y+dy[i]; 15 if(xx>n+1 || xx<0 || yy>n+1 || yy<0 || map[xx][yy]==1 ||vis[xx][yy]==1) continue; 16 map[xx][yy]=0; 17 vis[xx][yy]=1; 18 dfs(xx,yy); 19 //在這里不進行回溯 20 } 21 return ; 22 23 } 24 int main() 25 { 26 scanf("%d",&n); 27 for(int i=1;i<=n;i++) //從1開始存 28 { 29 for(int j=1;j<=n;j++) 30 { 31 scanf("%d",&map[i][j]); 32 if(map[i][j]==0) map[i][j]=2; 33 } 34 } 35 for(int i=0;i<n+1;i++) 36 { 37 map[i][0]=2; //添加的第一行 38 map[0][i]=2;//第一列 39 map[n+1][i]=2;//最后一行 40 map[n+1][i]=2; //最后一列· 41 42 } 43 dfs(0,0); 44 for(int i=1;i<=n;i++) //從1開始輸出,不管外面圍牆 45 { 46 for(int j=1;j<=n;j++) 47 { 48 printf("%d ",map[i][j]); 49 } 50 printf("\n"); 51 } 52 return 0; 53 }
-----------------------------------------------------------------------------------------------------------
P1141 01迷宮
題目描述
有一個僅由數字000與111組成的n×nn \times nn×n格迷宮。若你位於一格0上,那么你可以移動到相鄰444格中的某一格111上,同樣若你位於一格1上,那么你可以移動到相鄰444格中的某一格000上。
你的任務是:對於給定的迷宮,詢問從某一格開始能移動到多少個格子(包含自身)。
輸入格式
第111行為兩個正整數n,mn,mn,m。
下面nnn行,每行nnn個字符,字符只可能是000或者111,字符之間沒有空格。
接下來mmm行,每行222個用空格分隔的正整數i,ji,ji,j,對應了迷宮中第iii行第jjj列的一個格子,詢問從這一格開始能移動到多少格。
輸出格式
mmm行,對於每個詢問輸出相應答案。
輸入輸出樣例
2 2 01 10 1 1 2 2
4 4
1 #include <iostream> 2 #include <cstdio> 3 #include <queue> 4 using namespace std; 5 const int MAXM=100000; 6 const int MAXN=1000; 7 int n,m; 8 char map[MAXN][MAXN]; 9 int dx[4]={0,0,-1,1}; 10 int dy[4]={-1,1,0,0}; 11 int vis[MAXN][MAXN]; 12 int step[10000005][2]; 13 int ans; 14 void dfs(int x,int y) 15 { 16 if(x<0 || y<0 ||x>=n ||y>=n) return; 17 if(vis[x][y]) return; 18 step[ans][0]=x; //將點存儲 19 step[ans][1]=y; 20 vis[x][y]=1; 21 ans++; 22 for(int i=0;i<4;i++) 23 { 24 int xx=x+dx[i],yy=y+dy[i]; 25 if(map[xx][yy]==map[x][y]) continue; //如果是相同的兩個點,則跳過 26 dfs(xx,yy); 27 } 28 } 29 30 int main() 31 { 32 int a,b; 33 scanf("%d%d",&n,&m); 34 for(int i=0;i<n;i++) 35 scanf("%s",map[i]); //存圖 36 for(int i=0;i<m;i++) 37 { 38 scanf("%d%d",&a,&b); 39 a=a-1;b=b-1;// 因為存圖的時候是從(0,0)開始的,所以在搜索某一點時也需要減1 40 ans=0; 41 if(vis[a][b]>0) 42 printf("%d\n",vis[a][b]); //如果vis>0 說明已經訪問過了,並且已經存過了 43 if(vis[a][b]==0) 44 { 45 dfs(a,b); //如果沒有訪問過,搜索一遍 46 for(int i=0;i<ans;i++) 47 vis[step[i][0]][step[i][1]]=ans; //將它便利的連通的點的訪問數組都存為答案; 48 printf("%d\n",ans); 49 } 50 51 } 52 return 0; 53 }
用step數組存一個連通圖的所有的點。一次只能存一個連通圖的,最后將此連通圖中的結點的vis數組都存儲上ans;
這道題一開始想用模板套。但是發現如果用模板套的話,並沒有算上當前點。
-------------------------------------------------------
PCF55B
題意翻譯
現有4個整數(均小於等於1000),並給出三個運算符(均為+或*)。要求每次取出不一定相鄰的兩個數,並依次使用給出的運算符對這兩個數進行運算,並將結果當做一個新數如此操作,直到只剩下一個數為止。 編程求出最后剩下數的最小值。 翻譯貢獻者UID:22930
輸入輸出樣例
1 1 1 1 + + *
3
2 2 2 2 * * +
8
1 2 3 4 * + +
9
#include <iostream> #include <cstdio> #include <queue> #define ll long long using namespace std; int INF=0x7f7f7f7f7f7f7f7f; ll ans=INF; ll a[5]; char b[4]; void dfs(const int &l) { if(l==1) { ll s; for(int i=1;i<=4;i++) { if(a[i]!=-1) { s=a[i]; break; }// 找最后剩下的那個數 } ans=min(ans,s); return; } for(int i=1;i<=4;i++) { for(int j=i+1;j<=4;j++) { int k=4-l+1; ll x=a[i],y=a[j]; char t=b[k]; //cout<<"x :"<<x<<" y :"<<y<<" t: "<<t<<endl; if(x==-1 || t=='-' ||y==-1 ) continue; a[j]=-1,b[k]='-'; if(t=='+') a[i]=x+y; else a[i]=x*y; dfs(l-1); //cout<<"xx :"<<x<<" xy :"<<y<<" tt: "<<t<<endl; a[i]=x,a[j]=y,b[k]=t; //回溯 // cout<<"xxx :"<<x<<" xyx :"<<y<<" xtt: "<<t<<endl; } } } int main() { for(int i=1;i<=4;i++) scanf("%l64d",&a[i]); getchar(); //cout <<a[3]<<" "<<a[1]<<" "<<a[2]<<" "<<a[4]<<endl; for(int i=1;i<=3;i++) { scanf("%c",&b[i]); getchar(); } dfs(4); cout<<ans; return 0; }