[搜索] ZOJ1002、ZOJ1008、ZOJ1019、POJ1011

来源:互联网 发布:socket服务端 java 编辑:程序博客网 时间:2024/06/05 19:44

ZOJ1002

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1002

 

一个迷宫有一些围墙障碍,如果防止的两个碉堡可以直接连线且中间没有围墙挡住,则是不可以放置的。

因为数据范围小,直接DFS递归就好了。

用col,row表示该行和列是否可以放置,当碰到围墙的时候,围墙所在的行和列都变成可以放置了(因为递归是从上到下从左到右的,所以不会影响之前的情况。

 

 

#include<iostream>#include<cstdio>#include<vector>#include<string>using namespace std;int ans;void _cal(vector<string>& vs,int k,vector<int>& col,vector<int>& row,int cur){int x,y;int n=vs.size();if (k==n*n){ans=max(cur,ans);return;}x=k/n;y=k%n;if ( vs[x][y]=='X' ){int r=row[x];int c=col[y];row[x]=col[y]=0;_cal(vs,k+1,col,row,cur);row[x]=r;col[y]=c;}else{if ( row[x]==0&&col[y]==0 ) //can put{row[x]=col[y]=1;_cal(vs,k+1,col,row,cur+1);row[x]=col[y]=0;}_cal(vs,k+1,col,row,cur);}}void cal(vector<string>& vs){int n=vs.size();vector<int> col(n,0);vector<int> row(n,0);_cal(vs,0,col,row,0);}int main(){int n;while((cin>>n)&&n!=0){vector<string> vs;vs.reserve(n);string s;while(n--){cin>>s;vs.push_back(s);}ans=0;cal(vs);cout<<ans<<endl;}}


ZOJ 1008:

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=8

刚开始看觉得挺简单的搜索,然后看数据范围最大才5X5的格子,心想就算是暴力时间应该也差不多,于是先写了个毫无剪枝的搜索,理所当然超时了。

然后自己想了几个剪枝:

1.首先用一个大小为10的数组gLeft,数组中每个值记录的是 left == i 的格子编号,比如有3个格子的left=1 ,那么gLeft[1] 里保存这3个格子的编号值;

2.同理设置一个大小为10的数组gTop;

3.再设置一个大小为10的数组 nLeft,数组记录的是有多少个格子的 left ==i , 比如如条件1,初始时 nLeft[1] = 3;但是值会在搜索中变化;

4.同理设置nTop;


想法是这样的:

1.如果格子不在第一列,我们要在该位置放置一个格子,这个格子的left == 前一个格子的right ;

2.如果格子不在第一行,我们要在该位置放置一个格子,这个格子的top == 上一个格子的bottom;

3.所以既然知道前一个格子的 right (上一个格子的bottom),那么我们当前可以放置的格子就应当是  gLeft [right] , gTop[bottom] 里的这些格子,这样我们就不必每次都遍历所有的格子来尝试;

4.当我们决定在当前位置放置一个格子之后,如果它不在第一行,那么它的bottom肯定要被之后的某个格子匹配,如果不在第一列,它的right肯定要被某个格子的left匹配,所以我们用nLeft和nTop记录的每个值当前还有多少个格子可用,比如当前格子的right = 8,但是nLeft[8]=0,即left为8的格子全用完的,那么很显然当前这个格子肯定不应该放,也没有必要往下搜索了~


基本用了这两个思路来剪枝之后试了一下,结果还是超时,略有点失望,只好百度一下别人的思路。

发现结果别人只用了一个剪枝:就是除去重复格子,比如25个格子里有10个是相同的,那么搜索空间只是在15个格子里,这么简单一个剪枝效果却是巨大的!

思路是如果当前放格子A不行,那么很明显跟它相同的格子B当然也不可能行。

发下自己的代码,也提醒自己在搜索时除去冗余数据是多么的重要!

#include<iostream>#include<vector>#include<cstdio>#include<algorithm>using namespace std;struct grid{int bot,top,left,right;grid(int b=0,int t=0,int l=0,int  r=0):bot(b),top(t),left(l),right(r){}bool operator==(const grid& other)const{return bot==other.bot&&top==other.top&&left==other.left&&right==other.right;}};grid G[5][5];vector<grid> able;vector<int> ncount;int n;int t;bool test(int k){if ( k==n*n )return true;int x=k/n;int y=k%n;bool ok=false;for (int i=0;i<able.size();i++){if (ncount[i]==0)continue;G[x][y]=able[i];bool lok=true,tok=true;if(x>0)tok=G[x][y].top==G[x-1][y].bot;if(y>0)lok=G[x][y].left==G[x][y-1].right;if ( lok && tok ){ncount[i]--;ok=test(k+1);ncount[i]++;if ( ok )return ok;}}return ok;}int main(){int nCase=0;while(scanf("%d",&n)&&n){nCase++;t=n*n;able.clear();ncount.clear();for(int i=0;i<n;i++)for(int j=0;j<n;j++){int b,t,l,r;scanf("%d%d%d%d",&t,&r,&b,&l);grid tmp=grid(b,t,l,r);vector<grid>::iterator it=find(able.begin(),able.end(),tmp);if ( it==able.end()){able.push_back(tmp);ncount.push_back(1);}else{ncount[it-able.begin()]++;}}if (nCase > 1)printf("\n");printf("Game %d: ",nCase);if ( test(0) )printf("Possible\n");elseprintf("Impossible\n");}}

ZOJ1019:

http://acm.zju.edu.cn/onlinejudge/showRuns.do?contestId=1

读了题之后,心想也想不出什么剪枝来啊,结果写了一个没有剪枝的居然也没有超时,这数据太水了。

#include<cstdio>#include<iostream>using namespace std;typedef struct step{int x,y;char direction;}step;step steps[10000];int maps[101][101];int m,n,nSteps;bool move(int& x,int& y, char d){if (d=='U'){if ( x>1 && maps[x-1][y]==0 ){x--;return true;}return false;}if (d=='D'){if ( x<n && maps[x+1][y]==0 ){x++;return true;}return false;}if (d=='L'){if ( y>1 && maps[x][y-1]==0 ){y--;return true;}return false;}if (d=='R'){if ( y<m && maps[x][y+1]==0 ){y++;return true;}return false;}return false;}int walk(int x,int y,int k){if (x<1||x>n||y<1||y>m||maps[x][y]==1)return 0;if ( k==nSteps)return 1;int i=1;int tx=x;int ty=y;char dir=steps[k].direction;for(;i<=steps[k].x-1;i++){if(!move(tx,ty,dir))return 0;}for(;i<=steps[k].y;i++){if(move(tx,ty,dir)){if (walk(tx,ty,k+1))return 1;}elsebreak;}return 0;}int main(){int k,t;int i,j,tx,ty;int count=0;char ch;scanf("%d",&k);while(k--){t=0;count=0;nSteps=0;scanf("%d%d",&n,&m);for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&maps[i][j]);while(1){scanf("%d%d",&tx,&ty);if(tx==0 || ty==0)break;steps[t].x = tx;steps[t].y = ty;scanf(" %c",&ch);steps[t].direction = ch;t++;nSteps++;}for(i=1;i<=n;i++)for(j=1;j<=m;j++)count += walk(i,j,0);printf("%d\n",count);}return 0;}


ZOJ 1084

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=84

其实就是给相邻的城市间着色的问题,由于四色问题已经被证明过了,所以我们知道四色肯定是可以的。只需要检查1色(全不相连),2色和3色是否可以就行了。

搜索还是蛮简单的,先尝试给当前节点着色,然后查看所有相邻节点来检查该色是否有效,向下搜索即可。

#include<iostream>#include<cstdio>#include<cstring>using namespace std;bool g[26][26];int used[26];int n;bool dfs(int id,int col){if ( id==n)return true;bool flag;for(int i=0;i<col;i++){flag=true;used[id]=i;for(int j=0;j<n;j++)if (g[id][j]==1&&used[j]==i){flag=false;break;}if ( flag ){return dfs(id+1,col);}}return false;}int main(){int i,j;bool one;char adj[50];while(scanf("%d",&n)&&n){memset(g,0,sizeof(g));memset(used,0,sizeof(used));one =true;for(i=0;i<n;i++){scanf("%s",adj);for(j=2;adj[j];j++,one=false){g[i][adj[j]-'A']=1;g[adj[j]-'A'][i]=1;}}if (one)printf("1 channel needed.\n");else if (dfs(1,2))printf("2 channels needed.\n");else if (dfs(1,3))printf("3 channels needed.\n");elseprintf("4 channels needed.\n");}return 0;}



POJ1011 STICKS 搜索

#include <iostream>#include <algorithm>using namespace std;int sticks[64], n, len, num;bool used[64];bool compare(int a, int b){return a > b;}bool dfs(int cur, int left, int level) {//cur: 当前已经计算的木棒编号,left:该段还剩的长度,level:已经成功的木棒数if(left == 0) {//匹配一根木棒成功if(level == num-2)return true;for(cur = 0; used[cur]; cur++);used[cur] = true;if(dfs(cur+1, len-sticks[cur], level+1))return true;used[cur] = false;return false;} else {if(cur >= n-1)return false;for(int i = cur; i < n; i++) {if(used[i])continue;if((sticks[i] == sticks[i-1]) && !used[i-1])continue;if(sticks[i] > left)continue;used[i] = true;if(dfs(i, left-sticks[i], level))return true;used[i] = false;}return false;}}int main(){while(cin>>n) {if(n == 0)break;int sum = 0;for(int i = 0; i < n; i++) { scanf("%d", &sticks[i]); sum += sticks[i];}sort(sticks, sticks+n, compare); //由大到小排序bool end = false;for(len = sticks[0]; len <= sum/2; len++) {if(sum%len == 0) {used[0] = true;num = sum/len;if(dfs(0, len-sticks[0], 0)) {end = true;printf("%d\n", len);break;}used[0] = false;}}if(!end)printf("%d\n", sum);memset(used, 0, sizeof(used));}//system("pause");return 0;}