现在的位置: 首页 > 综合 > 正文

[搜索] ZOJ1002、ZOJ1008、ZOJ1019、POJ1011

2017年12月23日 ⁄ 综合 ⁄ 共 6099字 ⁄ 字号 评论关闭

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

 

【上篇】
【下篇】

抱歉!评论已关闭.