ZOJ 3675 Trim the Nails (状态压缩+BFS)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3675

题意:Robert用一个指甲刀剪指甲,但是他的指甲刀上有一些豁口,现在假设他的指甲刀宽Nmm(指甲刀的具体情况用“*”和“.”表示:“*”表示是好的地方,“.”表示是坏的地方,每个符号表示1mm),指甲宽Mmm。求Robert最少要剪多少下才可以把指甲剪完。PS:指甲刀可以正用,也可以反用。

思路:因为要剪完全部指甲用最少的次数则一定要从一侧开始剪,不可能出现先剪中间后剪两边比从一侧开始剪次数少的情况,所以剪得过程可以用贪心解决。这样就可以通过状态压缩用数字的2进制的01表示指甲和指甲刀的状态。这样只要保证指甲刀的最右端是完好的(右侧要是有不完好的部分忽略了就好了),每次剪得时候指甲的最右端是没剪过的(如果已经剪过也忽略就好了,就是不把它当指甲了),这样的贪心的剪,然后用BFS搜索就OK了。

位运算不熟啊……以后要熟悉一下了!看看有机会再做几道状态压缩的题~

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

struct node
{
	int nail;//指甲的状态(1表示没剪过的,0表示剪过的)
	int cnt;
}now,tmp;

bool vis[1050000];
int n,m,op1,op2;

int bfs()
{
	queue <struct node> q;
	now.nail=1;
	now.nail=(now.nail<<m)-1;//初值全为1//指甲没被剪的为1,被剪掉的为0
	now.cnt=0;
	q.push(now);
	vis[now.nail]=true;
	while(!q.empty())
	{
		now=q.front();
		q.pop();
		if(!now.nail)//等0时退出
			return now.cnt;
		//正着剪
		tmp=now;
		tmp.nail&=op1;//剪指甲
		while(tmp.nail&&((tmp.nail&1)==0))
			tmp.nail>>=1;//将末尾的已被剪掉的部分去掉(即将指甲移动到最右面是没剪的为止)
        if(!vis[tmp.nail])
        {
            tmp.cnt++;
            q.push(tmp);
            vis[tmp.nail]=true;
        }
		//反着剪
		tmp=now;
		tmp.nail&=op2;//剪指甲
		while(tmp.nail&&(tmp.nail&1)==0)//将末尾的已被剪掉的部分去掉(即将指甲移动到最右面是没剪的为止)
			tmp.nail>>=1;
		if(!vis[tmp.nail])
        {
            tmp.cnt++;
            q.push(tmp);
            vis[tmp.nail]=true;
        }
	}
	return -1;
}

int main()
{
	while(scanf("%d",&n)==1)
	{
		char ip[50];
		int i;
		scanf("%s",ip);
		op1=op2=(1<<21)-1;
		for(i=0;i<n;i++)
		{
			if(ip[i]=='*')
			{
				op1&=~(1<<i);
				op2&=~(1<<(n-i-1));
			}
		}//指甲刀(完好的部分为0,豁口为1)
		if(op1==(1<<21)-1)
		{
			scanf("%d",&m);
			printf("-1\n");
			continue;
		}
		while(op1&1)//将末尾的1去掉,保证指甲刀的最右面不是豁口
			op1=op1/2+1048576;
		while(op2&1)//将末尾的1去掉,保证指甲刀的最右面不是豁口
			op2=op2/2+1048576;
		scanf("%d",&m);
		memset(vis,false,sizeof(vis));
		printf("%d\n",bfs());
	}
	return 0;
}


你可能感兴趣的:(ZOJ 3675 Trim the Nails (状态压缩+BFS))