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;
}