搜索 水题&&错误集锦
引子:
本以为搜索的题目老师也不会检查,结果今天早上loli慢悠悠的说:“请同学们提交一下搜索的题目~”,顿时心旌摇曳,却也只能装作镇定自若的样子,点了点头。。
然后就开始了今天的疯狂做题,虽说题目都不是太难,但题多势众啊...
刷了那么多题,小有收获,总结+复习一下,也是为了以后避免类似错误(十分直观而又影响巨大的)
所做题目链接:openjudge 2.5 题面不一一附上
1.letters
看到这道题,就想到了前几天刚刚学会的STLmap映射,然后没怎么想就敲了一遍代码,结果跑的巨慢无比,然后下去跑操想明白了不用这么麻烦的做法,直接用一个vis数组就是了,看来长时间不做搜索大脑都僵化了。。
其他的就没什么技术含量了,和dfs模板差不多,
细节:1.运算种的i循环0-3就是4次
2.起始点的vis为true
3.A-Z ascill码为65-90
代码:
1 #include<iostream> 2 #include<cstring> 3 4 using namespace std; 5 6 int fx[4]={0,1,0,-1}; 7 int fy[4]={-1,0,1,0}; 8 int ans=0,d[200][200],n,m; 9 char a[25][25];bool vis[100]; 10 11 int dfs(int x,int y,int z) 12 { 13 ans=max(ans,z); 14 for(int i=0;i<4;i++)//细节 15 { 16 int xx=x+fx[i]; 17 int yy=y+fy[i]; 18 if(xx<=n&&xx>0&&yy<=m&&yy>0&&!vis[a[xx][yy]]) 19 { 20 vis[a[xx][yy]]=true; 21 dfs(xx,yy,z+1); 22 vis[a[xx][yy]]=false; 23 } 24 } 25 } 26 int main() 27 { 28 memset(vis,false,sizeof(vis)); 29 cin>>n>>m; 30 for(int i=1;i<=n;i++) 31 for(int j=1;j<=m;j++) 32 cin>>a[i][j]; 33 vis[a[1][1]]=true;//细节 34 dfs(1,1,1); 35 cout<<ans<<endl; 36 return 0; 37 }
2&&3.八皇后&&问题
例题八皇后问题引申出的各种问题,比较简单,+注意细节
细节:
1.横纵循环的次序,外层循环行号,内层循环列号
代码:
1 #include<iostream> 2 using namespace std; 3 int s=0,n,ans[20],h[100]={0},l[100]={0},a[100]={0},b[100]={0},anss[93][20]; 4 void out() 5 { 6 s++; 7 for(int i=1;i<=8;i++) 8 anss[s][i]=ans[i]; 9 } 10 int dfs(int k) 11 { 12 //cout<<"12 "; 13 for(int j=1;j<=8;j++) 14 { 15 if((h[j]==0)&&(a[k+j]==0)&&(b[k-j+8]==0)) 16 { 17 ans[k]=j; 18 h[j]=1; 19 a[k+j]=1; 20 b[k-j+8]=1; 21 22 if(k==8)out(); 23 else dfs(k+1); 24 h[j]=0; 25 a[k+j]=0; 26 b[k-j+8]=0; 27 } 28 } 29 } 30 int main() 31 { 32 dfs(1); 33 cin>>n; 34 for(int i=1;i<=n;i++) 35 { 36 int x; 37 cin>>x; 38 for(int k=1;k<=8;k++) 39 cout<<anss[x][k]; 40 cout<<endl; 41 } 42 }
1 #include<iostream> 2 using namespace std; 3 int s=0,n,ans[20],h[100]={0},l[100]={0},a[100]={0},b[100]={0},anss[93][20]; 4 void out() 5 { 6 s++; 7 // if(s>4)return ; 8 cout<<"No. "<<s<<endl; 9 for(int i=1;i<=8;i++) 10 { 11 for(int j=1;j<=8;j++) 12 { 13 if(ans[j]==i)cout<<1<<" "; 14 else 15 cout<<0<<" "; 16 } 17 cout<<endl; 18 } 19 } 20 int dfs(int k) 21 { 22 //cout<<"12 "; 23 for(int j=1;j<=8;j++) 24 { 25 if((h[j]==0)&&(a[k+j]==0)&&(b[k-j+8]==0)) 26 { 27 ans[k]=j; 28 h[j]=1; 29 a[k+j]=1; 30 b[k-j+8]=1; 31 32 if(k==8)out(); 33 else dfs(k+1); 34 h[j]=0; 35 a[k+j]=0; 36 b[k-j+8]=0; 37 } 38 } 39 } 40 int main() 41 { 42 // cin>>n;//cout<<"123"; 43 dfs(1); 44 cin>>n; 45 // cout<<s; 46 for(int i=1;i<=n;i++) 47 { 48 int x; 49 cin>>x; 50 for(int k=1;k<=8;k++) 51 cout<<anss[x][k]; 52 cout<<endl; 53 } 54 }
4.迷宫
这道题是我做openjudge交的最多的一道题,足足交了30次,自己的dfs不知道出现了什么问题,就是不对,总是5分。。。
看了看提问区,五分的解决方法就是判断起点和终点,可是我明明判断了,无奈。。。
唯一的细节就是题目给出的特判,所以收获也就只有好好看题。
5.红与黑
又是一道模板题,注意的地方就是输入的行列是反着来的,
代码:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int a[25][25]; 6 int dx[5]={0,0,1,-1},dy[5]={1,-1,0,0}; 7 int m,n,ans=0,xe,ye; 8 9 void dfs(int x,int y) 10 { 11 ans++; 12 a[x][y]=0; 13 for(int i=0;i<=3;i++) 14 { 15 int xx=x+dx[i]; 16 int yy=y+dy[i]; 17 if(xx<=m&&xx>0&&yy<=n&&yy>0&&a[xx][yy]==1) 18 { 19 a[xx][yy]=0; 20 dfs(xx,yy); 21 } 22 } 23 } 24 25 int main() 26 { 27 while(cin>>n>>m) 28 { 29 memset(a,0,sizeof(a)); 30 ans=0; 31 if(m==0&&n==0)break; 32 for(int i=1;i<=m;i++) 33 for(int j=1;j<=n;j++) 34 { 35 char x; 36 cin>>x; 37 if(x=='.') 38 a[i][j]=1; 39 if(x=='@') 40 { 41 a[i][j]=1; 42 xe=i,ye=j; 43 } 44 } 45 dfs(xe,ye); 46 cout<<ans<<endl; 47 } 48 return 0; 49 }
6.棋盘问题
这道题第一次交的时候WA0分,主要是因为没好好看题,'#' 的地方才可以放棋子,可是受了上一道题的影响,+盲目自信没试样例2导致了如此惨剧的发生
收获:好好看题X2!!
7.取石子游戏
这明明就是递归,不知道为什么放到搜索上来= =
注意每次是谁先手,这里num=1为player1放棋子,player1设为先手拿旗子,
用3目运算符小装了一手,感觉良好,
这道题写了两遍,主要注意取到0获胜的到底是递归上一层的player还是这一层的player,总归最后还是理清了思路。
代码:
1 #include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <algorithm> 5 6 using namespace std; 7 int f(long long a,long long b,int num) 8 { 9 if(a<b)swap(a,b); 10 if(b==0)return num==1?2:1; 11 if(a/b>=2)return num; 12 a-=b; 13 return f(a,b,num==1?2:1); 14 } 15 int main() 16 { 17 long long a,b; 18 while(cin>>a>>b) 19 { 20 if(a==0&&b==0)break; 21 if(f(a,b,1)==1) 22 cout<<"win"<<endl; 23 else 24 cout<<"lose"<<endl; 25 } 26 }
8.马走日
和dfs模板略有不同的特点可能是八个方向吧,每次走到ans++就好,一次AC
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 7 int m,n,ans; 8 int dx[10]={1,1,2,2,-1,-1,-2,-2},dy[10]={2,-2,1,-1,2,-2,1,-1}; 9 int vis[15][15]; 10 void dfs(int x,int y,int s) 11 { 12 if(s>n*m) 13 { 14 ans++; 15 return; 16 } 17 for(int i=0;i<8;i++) 18 { 19 int xx=x+dx[i]; 20 int yy=y+dy[i]; 21 if(xx>=0&&xx<=n-1&&yy>=0&&yy<=m-1) 22 if(!vis[xx][yy]) 23 { 24 vis[xx][yy]=1; 25 dfs(xx,yy,s+1); 26 vis[xx][yy]=0; 27 } 28 } 29 } 30 int main() 31 { 32 int u,a,b; 33 cin>>u; 34 while(u--) 35 { 36 memset(vis,0,sizeof(vis)); 37 cin>>n>>m>>a>>b; 38 ans=0; 39 vis[a][b]=1; 40 dfs(a,b,2); 41 cout<<ans<<endl; 42 } 43 }
9.单词接龙
记得当时刚来集训,和Juan_feng一起讨论后A掉了这道题(多亏我机智的+1,虽然不知道原理),高兴了一手,
不过听loli讲我们这样做的效率很低,直接把能不能连接,连接后长度增加多少,先预处理出一个二维数组就可以,不用每次都搜索,效率会快很多,深思熟虑后确实是这样,不过代码是之前的版本,有点小懒
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 7 int m,n,ans; 8 int dx[10]={1,1,2,2,-1,-1,-2,-2},dy[10]={2,-2,1,-1,2,-2,1,-1}; 9 int vis[15][15]; 10 void dfs(int x,int y,int s) 11 { 12 if(s>n*m) 13 { 14 ans++; 15 return; 16 } 17 for(int i=0;i<8;i++) 18 { 19 int xx=x+dx[i]; 20 int yy=y+dy[i]; 21 if(xx>=0&&xx<=n-1&&yy>=0&&yy<=m-1) 22 if(!vis[xx][yy]) 23 { 24 vis[xx][yy]=1; 25 dfs(xx,yy,s+1); 26 vis[xx][yy]=0; 27 } 28 } 29 } 30 int main() 31 { 32 int u,a,b; 33 cin>>u; 34 while(u--) 35 { 36 memset(vis,0,sizeof(vis)); 37 cin>>n>>m>>a>>b; 38 ans=0; 39 vis[a][b]=1; 40 dfs(a,b,2); 41 cout<<ans<<endl; 42 } 43 }
10.分成互质组
一遍ac,小开心一手,
dfs每个数是加入之前的已经建立的组别 or 新开一个组
搜完统计一下组数就是了,
然后还有个剪枝:如果当前组别已经大于目前最优解就return,因为不可能对答案有贡献了,
这道题很怪异的是,我居然自己能把自己的错误卡掉,虽然效率低了一点。。
本来就打算拿部分分的,毕竟从来没有错误再卡掉的操作,结果ac了,有点开心
代码:
1 #include <iostream> 2 #define re register 3 #include <algorithm> 4 5 using namespace std; 6 int n,a[15],vis[15],ans=15,tot[15],z[15][15]; 7 8 int gcd(int x,int y) 9 { 10 if(y==0)return x; 11 else 12 return gcd(y,x%y); 13 } 14 15 void dfs(int k,int s) 16 { 17 if(s>=ans)return ; 18 if(k>n) 19 { 20 int pd=0; 21 for(int i=0;i<=s;i++) 22 pd+=tot[i]; 23 if(pd!=n)return;//卡掉自己不知道为什么会出现的错误 24 ans=min(ans,s+1); 25 return ; 26 } 27 for(re int i=0;i<=s;i++) 28 { 29 bool flag=true; 30 for(int l=1;l<=tot[i];l++) 31 { 32 if(gcd(z[i][l],a[k])!=1) 33 { 34 flag=false; 35 break; 36 } 37 } 38 if(flag) 39 { 40 tot[i]++; 41 z[i][tot[i]]=a[k]; 42 dfs(k+1,s); 43 tot[i]--; 44 } 45 } 46 s++; 47 // cout<<s; 48 z[s][1]=a[k]; 49 tot[s]=1; 50 dfs(k+1,s); 51 s--; 52 tot[s]=0; 53 } 54 55 int main() 56 { 57 cin>>n; 58 for(int i=1;i<=n;i++) 59 cin>>a[i]; 60 dfs(1,0); 61 // cout<<"daole"; 62 cout<<ans; 63 }
11.分苹果
又是一道递归题,,
在OJ的递归和递推都有这道题,思维难度还是有的
代码:
1 #include<iostream> 2 using namespace std; 3 int put(int x,int y) 4 { 5 if((x==1)||(y==1)||(y==0)) 6 return 1; 7 if(x<y) 8 return put(x,x); 9 if(x==y) 10 return put(x,y-1)+1; 11 if(x>y) 12 return put(x,y-1)+put(x-y,y); 13 } 14 main() 15 { 16 int n,x,y; 17 cin>>n; 18 19 for (int i=1;i<=n;i++) 20 { 21 cin>>x>>y; 22 cout<<put(x,y)<<endl; 23 } 24 return 0; 25 }
啊终于写完了,
dfsOJ搜索水题的总结如上