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"); else printf("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; } else break; } 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"); else printf("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; }