杭电2209 翻纸牌游戏 DFS BFS 搜索

19 篇文章 0 订阅
13 篇文章 0 订阅

翻纸牌游戏

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3617    Accepted Submission(s): 1356


Problem Description
有一种纸牌游戏,很有意思,给你N张纸牌,一字排开,纸牌有正反两面,开始的纸牌可能是一种乱的状态(有些朝正,有些朝反),现在你需要整理这些纸牌。但是麻烦的是,每当你翻一张纸牌(由正翻到反,或者有反翻到正)时,他左右两张纸牌(最左边和最右边的纸牌,只会影响附近一张)也必须跟着翻动,现在给你一个乱的状态,问你能否把他们整理好,使得每张纸牌都正面朝上,如果可以,最少需要多少次操作。
 

Input
有多个case,每个case输入一行01符号串(长度不超过20),1表示反面朝上,0表示正面朝上。
 

Output
对于每组case,如果可以翻,输出最少需要翻动的次数,否则输出NO。
 

Sample Input
  
  
01 011
 

Sample Output
  
  
NO 1
DFS:
#include<stdio.h>  
#include<string.h>  
#define INF 1<<30  
int a[25],cnt[25];  
int ans,temp,len;  
bool myok()  
{  
   int i;  
   for(i=0;i<len;i++)  
     if(a[i]) return false;  
   return true;  
}  
void dfs(int idx)  
{  
    int i;  
   if(myok())//判断是否还有反面的纸牌,若有则继续翻牌,无则计数  
   {  
       temp=0;  
       for(i=0;i<len;i++)  
           if(cnt[i]==1) temp++;  
       if(temp<ans)  
           ans=temp;  
       return;  
   }  
   if(idx>=len) return;//当搜索完最后一张必须返回  
   for(cnt[idx]=0;cnt[idx]<2;)  
   {  
       a[idx]^=1;//翻牌  
       if(idx>0) a[idx-1]^=1;//翻左边的  
       if(idx<len) a[idx+1]^=1;//翻右边的  
       cnt[idx]++;  
       dfs(idx+1);  
   }  
}  
int main()  
{  
   int i;  
   char ch[25];  
   while(~scanf("%s",ch))  
   {  
     len=strlen(ch);  
     for(i=0;i<len;i++)  
        a[i]=ch[i]-'0';  
     ans=INF;  
     memset(cnt,0,sizeof(cnt));  
     dfs(0);  
     if(ans!=INF) printf("%d\n",ans);  
     else printf("NO\n");  
   }  
   return 0;  
}
方法二:BFS
思路:这里是利用到了我们的位运算,枚举了所有的可能情况,然后判断此情况是否已经出现过,入队并标记;
直到最后变为0即满足条件;
#include<stdio.h>
#include<string.h>
#include<queue>
#define N 1<<20//所有情况的最大值为2的20次方
using namespace std; 
int book[N],len;//book为标记数组,判断当前状态是否已经出现过;
struct node
{   int s;
    int step;
};
queue<node>Q;
int bfs(int x)
{   int i;
    node q,p;
    while(!Q.empty())
      Q.pop();
     q.s=x;
     q.step=0;
     book[x]=1;
     Q.push(q);
     int item,tmp;
     while(!Q.empty())
     {   p=Q.front();
         Q.pop();
         item=p.s;
         if(item==0)//当前为0时则已经完成了翻转
         {  return p.step;
		 }
		 for(i=1;i<=len;i++)
		 {   if(i==1)
		      tmp=item^3;//先翻转最右边的两张牌,这里从左往右从右往左不影响结果,由于我们
无法确定该二进制数的位数,所以我们直接从右往左翻;
		      else
		      tmp=item^(7<<(i-2));//依次翻转三张牌;
		     if(i==len)//这个是因为我们最左端的一张牌它前面没有牌,而我们还是按照三张牌去
翻转的它,这时候我们就需要去消除最左端的1,那是怎么消除的呢,
举个例子:1111 如果翻转最左端1时变为 10011 多了一位1,我们需要的结果是0011 那我们就让他和1111与一下,1111前面会多一个0,
		      tmp&=(1<<len)-1;
		      if(!book[tmp])//如果该状态未出现过则入队,
		      {    book[tmp]=1;
		           q.s=tmp;
		           q.step=p.step+1;//步数+1;
		           Q.push(q);
			  }
		 }
	 }
	 return -1;
     
}
int main()
{     int i,t;
      char s[25];
      while(scanf("%s",s)!=EOF)
      {    len=strlen(s);
           t=0;
           for(i=0;i<len;i++)
             t=(t<<1)+s[i]-'0';//将字符串转化为二进制数;
             //printf("%d ",t);
             memset(book,0,sizeof(book));
             int ans=bfs(t);
             if(ans==-1)
             printf("NO\n");
             else
             printf("%d\n",ans);
			   
	  }
	  return 0;
  
}



  
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值