hdu 2209 翻纸牌游戏 模拟||bfs

C - 翻纸牌游戏
Time Limit:3000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

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

Input

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

Output

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

Sample Input

01
011

Sample Output

NO
1
这个题有点类似于我做的poj3279 http://blog.csdn.net/howardemily/article/details/52966647
这个题还简单是一行的;两道题的思维差不多
思路:题中说每翻一张牌它的相邻左右两张牌也会跟着翻转一次;所以如果当前牌左侧的牌为0,该牌就不需要
翻转,如果当前左侧牌为1,那么该牌就一定要翻转,而且该牌右边的牌也必须要翻转;注意最后一张牌只能翻它
左面的那张牌了。
而对于第一张纸牌会有两种情况,翻转和不翻转,可能有人读者会有疑问,为什么会有两种情况,这里我说一点点
我的理解。
这种情况是由于最左端和最右端的两张牌只能影响到自身,和与它相邻的一张牌,而除了两端的牌其余的牌每翻转
一次,都要影响它自己和与它相邻的两张牌;
举个例子:对于1100 如果还按照我们所翻的规律去翻转,不翻第一张牌,那我们需要很麻烦去完成翻转,如果直接
翻转第一个就直接完成了翻转;
所以我们需要对第一张牌翻或不翻两种情况去进行模拟找到最小解;
ac代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
//0110;
int a[25],b[25];
int numA,numB,len,flag;
void solveA()//第一张牌不翻转
{   int  i,num=0; 
   for(i=1;i<len;i++)
    {   if(a[i-1]==1)
        {     a[i-1]^=1;
              a[i]^=1;
              if(i!=len-1)
              {   a[i+1]^=1;
			  }
			  
			num++;
		}
		
	}
	if(a[len-1]==0)
	 {   	numA=num;
	       flag=1;
	 }
    
}
void solveB()//第一张牌翻转
{    int i, num=0;
     b[0]^=1;
     b[1]^=1;
     num++;
     for(i=1;i<len;i++)
     {    if(b[i-1]==1)
           {    b[i-1]^=1;
                b[i]^=1;
                if(i!=len-1)
                 b[i+1]^=1;
                num++;
		   }
	 }
	 if(b[len-1]==0)
	 {    numB=num;
	      flag=1;
	 }
	 return ;
}
int main()
{    char s[25]; 
     int i;
     while(scanf("%s",s)!=EOF)
     {    flag=0;
          numA=numB=0xfffffff;
	      len=strlen(s);
	     for(i=0;i<len;i++)
          {    a[i]=b[i]=s[i]-'0';
		  }
		  solveA();
		  solveB();
		  if(flag==0)
		    printf("NO\n");
		  else
		    {   int ans=min(numA,numB);
		        printf("%d\n",ans);
			}
		  
	 }
	 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;
  
}
 
    

 
    
 
   
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值