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

来源:互联网 发布:linux下语言包安装 编辑:程序博客网 时间:2024/05/29 16:41
C - 翻纸牌游戏
Time Limit:3000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
Submit Status Practice HDU 2209

Description

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

Input

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

Output

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

Sample Input

01011

Sample Output

NO1
这个题有点类似于我做的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 0