【暑假集訓專題#搜索】
poj 2386 Lake Counting
【題意】:
有一個大小為N×M的園子,雨後積起了水。八連通的積水被覺得是連接在一起的。
請求出園子裏總共同擁有多少水窪?(八連通指的是下圖中相對W 的*的部分)
Sample Input
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
Sample Output
3
【思路】:
從隨意的W開始,不停地把鄰接的部分用‘.‘取代。1次DFS後與初始的這個W連接的全部W就都被替換成了‘.‘,因此直到圖中不再存在W為止,總共進行DFS的次數就是答案了。8個方向共相應了8種狀態轉移,每一個格子作為DFS的參數至多被調用一次,所以復雜度為O(8×N×M)=O(N×M)。
代碼:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int N=105; int n,m; char mat[N][N]; int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; bool ok(int dx,int dy){ if(dx>=0&&dx<n&&dy>=0&&dy<m) return true; return false; } void dfs(int x,int y){ mat[x][y]=‘.‘; for(int i=0; i<8; ++i){ int dx=x+dir8[i][0]; int dy=y+dir8[i][1]; if(ok(dx,dy)&&mat[dx][dy]==‘W‘)dfs(dx,dy); } return ; } int main() { while(scanf("%d%d",&n,&m)!=EOF){ for(int i=0; i<n; ++i) scanf("%s",mat[i]); int res=0; for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ if(mat[i][j]==‘W‘){ dfs(i,j); res++; } } } printf("%d\n",res); } return 0; }
【題目鏈接】:click here~~
【題意】:
Description
在一個給定形狀的棋盤(形狀可能是不規則的)上面擺放棋子,棋子沒有差別。要求擺放時隨意的兩個棋子不能放在棋盤中的同一行或者同一列,請編程求解對於給定形狀和大小的棋盤,擺放k個棋子的全部可行的擺放方案C。Input
輸入含有多組測試數據。
每組數據的第一行是兩個正整數。n k,用一個空格隔開,表示了將在一個n*n的矩陣內描寫敘述棋盤。以及擺放棋子的數目。 n <= 8 , k <= n
當為-1 -1時表示輸入結束。
隨後的n行描寫敘述了棋盤的形狀:每行有n個字符,當中 # 表示棋盤區域。 . 表示空白區域(數據保證不出現多余的空白行或者空白列)。
Output
對於每一組數據。給出一行輸出。輸出擺放的方案數目C (數據保證C<2^31)。Sample Input
2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1
Sample Output
2
1
【思路】:
這道題目類似N皇後問題,與之不同的是每一行不一定有棋盤,所以dfs裏要註意不一定是當前行。
思路非常easy。僅僅需從第一行第一個開始搜索,假設該位置該列沒被標記且為棋盤,那麽在這裏放上棋子。並標記。由於每行每列不能沖突,所以搜索下一行,比而且棋子數加1。每次搜索之前先要推斷是否棋子已經用完,假設用完。記錄方案數加1,然後直接返回。直到所有搜索所有完畢,此時已得到所有方案數。
此題還需註意標記數組僅僅標記某一列上是否有棋子。由於每次遞歸下一行,所以每一行不會有沖突,僅僅需推斷這一列上是否有其它棋子。
還要註意改動標記後遞歸回來要及時復原。
代碼:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int N=20; int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}}; int num,line,res; char mat[N][N]; bool vis_row[N]; void dfs(int _line,int now_num) { if(now_num==num){///棋子所有放置 ++res; return; } for(int i=_line; i<line; ++i){ for(int j=0; j<line; ++j) if(mat[i][j]==‘#‘&&!vis_row[j]){ vis_row[j]=true; dfs(i+1,now_num+1); vis_row[j]=false; } } } int main() { while(~scanf("%d%d",&line,&num)&&line!=-1&&num!=-1){ memset(vis_row,false,sizeof(vis_row)); for(int i=0; i<line; i++) scanf("%s",mat[i]); res=0; dfs(0,0); ///按行搜索,從0行遞增;當前放置的棋子 printf("%d\n",res); } return 0; }
POJ 3278 catch the cow
【題目鏈接】:click here~~
【題目大意】:給你兩個數 pre,last。共同擁有三種操作+1,-1,*2,使得pre變為last的最小步數。
【解題思路】:
常規方法:放入到一個隊列裏。每次,push,pop出最先入隊的數,然後依據三種可能變化入隊,中間設一個標記數組。表示變化到該步已經標記了,防止出現4-5-4的可能情況,然後中間一旦出現到目標數,跳出,輸出步驟數
代碼:
#include <stdio.h> #include <queue> #include <set> #include <stack> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> #include <bits/stdc++.h> using namespace std; const int N=2*1e6+10; typedef long long LL; typedef unsigned long long LLU; int n,m,pre,tp; int last,ans,cnt,res;///大數組一般定義在全局 int nx[N]; ///當前初始值變化的值 int ny[N]; ///值為tp的步數 queue<int >vall; typedef class { public : int val,step; private: int s1,s2; } pos; bool vis[N]; pos que[N]; int main() { while(~scanf("%d%d",&pre,&last)) { /* memset(vis,false,sizeof(vis)); bfs(); memset(nx,0,sizeof(nx)); memset(ny,0,sizeof(ny)); vall.push(pre); ///初始值壓入棧中,5 nx[pre]=0; ///達到當前變化的的值須要的步數,nx[5]=0, while(vall.size()!=0) { /// cout<<"vall.size()=" <<vall.size()<<endl; /// cout<<"vall.front()="<<vall.front()<<endl; tp=vall.front(); ///當前變化到的值 /// cout<<"[tp]"<<tp<<endl; /// cout<<"ny[tp]="<<ny[tp]<<endl; /// cout<<"nx[tp]="<<nx[tp]<<endl; vall.pop(); ny[tp]=1; ///走到該步值是否訪問。防止5-4-5的情況,ny[5]=1,ny[4]=1, if(tp==last) break;///走到目標數。跳出 if(tp-1>=0&&ny[tp-1]==0) { vall.push(tp-1);///4,3 nx[tp-1]=nx[tp]+1;///nx[4]=1,nx[3]=2; ny[tp-1]=1; ///ny[4]=1,ny[3]=1; } if(tp+1<=1e7&&ny[tp+1]==0) { vall.push(tp+1);///6 nx[tp+1]=nx[tp]+1;///nx[6]=1 ny[tp+1]=1; ///ny[6]=1 } if(tp*2<=1e7&&ny[tp*2]==0) { vall.push(tp*2);///10,8 nx[tp*2]=nx[tp]+1;///nx[10]=1,nx[8]=2; ny[tp*2]=1; ///ny[10]=1;ny[10]=1; } } printf("%d\n",nx[tp]);///輸出步數 } return 0; } 5 17 5--10--9--18--17 4
隊列+廣搜。事實上也是隊列
把三種可能情況也增加到還有一個隊列裏
代碼:
#include <math.h> #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define lowbit(a) a&-a #define Max(a,b) a>b?a:b #define Min(a,b) a>b?b:a #define mem(a,b) memset(a,b,sizeof(a)) int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}}; const int N=2*1e6+10; int n,m,pre,last; bool vis[N]; typedef long long LL; typedef unsigned long long LLU; struct node{ int val; int step; }nod; void bfs(){ memset(vis,0,sizeof(vis)); node top,tmp; nod.val=pre; nod.step=0; queue<node >vall; while(!vall.empty()) vall.pop(); vall.push(nod); while(!vall.empty()){ top=vall.front(),vall.pop(); if(top.val==last){ printf("%d\n",top.step); return ; } if(top.val*2<=N&&vis[top.val*2]==0){ tmp.val=top.val*2; tmp.step=top.step+1; vis[top.val*2]=1; vall.push(tmp); } if(top.val-1>=0&&vis[top.val-1]==0){ tmp.val=top.val-1; tmp.step=top.step+1; vis[top.val-1]=1; vall.push(tmp); } if(top.val+1<=last&&vis[top.val+1]==0){ tmp.val=top.val+1; tmp.step=top.step+1; vis[top.val+1]=1; vall.push(tmp); } } } int main() { while(~scanf("%d%d",&pre,&last)){ bfs(); } return 0; }
hdu 1241
【題目鏈接】click here~~
[email protected],‘*‘代表地面,八個方向相鄰的油田視為一個,求給定地圖裏油田數目
【解題思路】八個方向搜索就可以
和poj2386 類似的
代碼:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int N=1010; int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; char mat[N][N]; int row,line; bool ok(int dx,int dy){ if(dx>=0&&dx<=row&&dy>=0&&dy<=line) return true; return false; } void dfs(int x,int y){ mat[x][y]=‘*‘; for(int i=0; i<8; ++i){ int dx=x+dir8[i][0]; int dy=y+dir8[i][1]; if(ok(dx,dy)&&mat[dx][dy][email protected]){ dfs(dx,dy); } } } int main() { while(~scanf("%d%d",&row,&line)&&row&&line){ for(int i=0; i<row; ++i) scanf("%s",mat[i]); int res=0; for(int i=0; i<row; ++i){ for(int j=0; j<line; ++j){ if(mat[i][j][email protected]){ dfs(i,j); res++; } } } printf("%d\n",res); } return 0; } /* 1 1 * 3 5 *@*@* **@** *@*@* 1 8 @@****@* 5 5 ****@ *@@*@ *@**@ @@@*@ @@**@ 0 0 */
poj 2251 Dungeon Master
【題目鏈接】:click here~~
【題意】:
你被困在一個三維地牢,地牢組成單元是一個 立方體,其能夠或能夠不被填充的巖石。每次你能夠它一分鐘移動一個單元,從北,南。東,西,向上或向下。你不能移動對角線和迷宮的四面由固體巖石。是否能逃避。假設能輸出最短時間。
【思路】:事實上就是二維廣搜的加強版,無非多加了一維。加個推斷就能夠了。
代碼:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define rep(i,j,k) for(int i=(int)j;i<(int)k;++i) #define per(i,j,k) for(int i=(int)j;i>(int)k;--i) #define lowbit(a) a&-a #define Max(a,b) a>b?a:b #define Min(a,b) a>b?b:a #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef unsigned long long LLU; typedef double db; const int N=35; const int inf=0x3f3f3f3f; int L,R,C,n,m,t,ans,res,cnt,tmp; char str[N]; bool vis[N][N][N]; char mat[N][N][N];///map int dir6[6][3]= {{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};///六個方向 int pre_i,pre_j,pre_k; int last_i,last_j,last_k; struct node{ int x,y,z,step; }; bool ok(int dx,int dy,int dz){ if(dx>=0&&dx<L&&dy>=0&&dy<R&&dz>=0&&dz<C) return true; return false; } void getMat(){ for(int l=0; l<L; ++l){ for(int row=0; row<R; ++row){ scanf("%s",mat[l][row]); for(int line=0; line<C; ++line){ /// if the ‘S‘ begin if(mat[l][row][line]==‘S‘){ pre_i=l,pre_j=row,pre_k=line; } /// if the ‘E‘ end if(mat[l][row][line]==‘E‘){ last_i=l,last_j=row,last_k=line; } } } } } int bfs(){ node a,b; queue <node>vall; a.x=pre_i; a.y=pre_j; a.z=pre_k; a.step=0; vis[pre_i][pre_j][pre_k]=true; vall.push(a); while(!vall.empty()){ a=vall.front(); vall.pop(); /// come to the situation if(a.x==last_i&&a.y==last_j&&a.z==last_k) return a.step; /// the six diretion for(int i=0; i<6; ++i){ b=a; b.x=a.x+dir6[i][0]; b.y=a.y+dir6[i][1]; b.z=a.z+dir6[i][2]; if(ok(b.x,b.y,b.z)&&!vis[b.x][b.y][b.z]&&mat[b.x][b.y][b.z]!=‘#‘){ vis[b.x][b.y][b.z]=true; b.step=a.step+1; vall.push(b); } } } return 0; } int main() { while(scanf("%d%d%d",&L,&R,&C)&&L&&R&&C){ memset(vis,false,sizeof(vis)); getMat(); int res=bfs(); if(res) printf("Escaped in %d minute(s).\n",res); else puts("Trapped!"); } return 0; }
poj 1426:
【題目鏈接】:click here~~
【題目大意】:
給出一個數n。找出一個數要求是n的倍數。而且這個數的十進制僅僅由1和0組成,明顯這種數不止一個(假設,滿足條件一定會有m×10也滿足,故不止一種),題目要求輸出隨意一個滿足該條件的m
對於數據1,可知2×5=10,故答案能夠得出是10(當然,100,1000...也滿足,可是special judge。僅僅用輸出一個滿足條件的解),其它數據也同理。
【思路】:
對於數據1。可知2×5=10,故答案能夠得出是10(當然,100。1000...也滿足,可是special judge,僅僅用輸出一個滿足條件的解),其它數據也同理。
代碼:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; int n; bool ok; void dfs(LL now,int deep) { if(ok) return ;///find the answer if(deep>=19) return;/// deep >20 if(now>=n&&now%n==0){ /// find the answer ok=1; printf("%lld\n",now); return ; } dfs(now*10,deep+1);/// dfs now*10 dfs(now*10+1,deep+1);///dfs now*10+1 } int main() { while(cin>>n&&n){ ok=0; dfs(1,0); } return 0; }
poj 3126 Prime Path
【題目鏈接】:click here~~
【題目大意】:給你兩個素數,求從n變化到m,每一步僅僅能改變一位上的一個數字,且每次改變得到的數也是素數,求最小的步數
【思路】:暴力枚舉個十百千的位數。註意各位要求是素數則僅僅枚舉奇數
代碼:
//poj 3126 #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define rep(i,j,k) for(int i=(int)j;i<(int)k;++i) #define per(i,j,k) for(int i=(int)j;i>(int)k;--i) #define lowbit(a) a&-a #define Max(a,b) a>b?a:b #define Min(a,b) a>b?b:a #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef unsigned long long LLU; typedef double db; const int N=15000; const int inf=0x3f3f3f3f; int n,m,T; bool vis[N]; int dir4[4][2]= {{1,0},{0,1},{-1,0},{0,-1}}; int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; int dir6[6][3]= {{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};///六個方向 inline LL read() { int c=0,f=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){c=c*10+ch-‘0‘;ch=getchar();} return c*f; } bool isPrime(int t)///素數推斷 { for(int i=2; i<=sqrt(t); ++i) if(t%i==0) return false; return true; } struct node { int val,step; }; void bfs() { mem(vis,false); node a,b; a.val=n; a.step=0; queue<node >vall; vall.push(a); while(!vall.empty()){ a=vall.front(); vall.pop(); if(a.val==m){ printf("%d\n",a.step); return ; } int ge=a.val%10;///枚舉個位 int shi=a.val%100/10;///枚舉十位 for(int i=1; i<=9; i+=2){///個位 int y=(a.val/10)*10+i; if(y!=a.val&&isPrime(y)&&!vis[y]){ b.val=y; b.step=a.step+1; vis[y]=1; vall.push(b); } } for(int i=0; i<=9; i++){///十位 int y=(a.val/100)*100+i*10+ge; if(y!=a.val&&isPrime(y)&&!vis[y]){ b.val=y; b.step=a.step+1; vis[y]=1; vall.push(b); } } for(int i=0; i<=9; i++){///百位 int y=(a.val/1000)*1000+i*100+ge+shi*10; if(y!=a.val&&isPrime(y)&&!vis[y]){ b.val=y; b.step=a.step+1; vis[y]=1; vall.push(b); } } for(int i=1; i<=9; i++){///千位 int y=a.val%1000+i*1000; if(y!=a.val&&isPrime(y)&&!vis[y]){ b.val=y; b.step=a.step+1; vis[y]=1; vall.push(b); } } } puts("Impossible"); return ; } int main() { T=read(); while(T--){ n=read(),m=read(); if(n==m) puts("0"); else bfs(); } return 0; } /* Sample Input 3 1033 8179 1373 8017 1033 1033 Sample Output 6 7 0 */
【暑假集訓專題#搜索】