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