杭电2209 翻纸牌游戏

翻纸牌游戏

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


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

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

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

Sample Input
01
011
 
Sample Output
NO
1
解题思路:看到这个题,首先要想清楚每一张牌有哪些属性。牌的正面反面是一种属性,翻牌又是另一种属性。所以每张牌都有两个属性,第一个就是牌的正反面,另一个就是对这张牌进行的操作,翻还是不翻。然后再想一个问题就是每翻一张牌,都会影响相邻的牌。那要怎样翻才能翻到头?每次翻总会影响已经翻好的牌。对于这个问题,其实非常容易解决,只需要翻后面的一张牌就可以解决。假如第二张牌目前是反面,想要让他翻成正面,而且不影响以前的牌,这时候只要把它后面的牌翻了即可。利用翻第三张牌产生的副作用来把第二张牌翻过去,这样就不会影响第一张牌的状态。同样,对于其他牌也是一样。因此这样就产生一种方法:从左到右依次翻,每次遇到需要改变的牌,总是翻其后面的一张。

解题之前需要明白这几个问题:

1.对于一组数据,最坏情况下翻多少次牌才能出来结果?

 答:假设数据中有n张牌,那么最多的翻牌次数为n。因为一张牌只有两个面,所以一张牌只能翻一次,

翻两次以后和没有翻是一样的。

2.每一张牌最多翻多少次?所有的牌有多少种翻的方法?

 答:每张牌最多翻一次,每张牌可以选择翻或者不翻,有两种状态。所有的牌如果都把这两种状态枚

举出来,肯定可以找到解。但是这么做需要枚举2的n次方,题目中的n最大是20,暴力枚举会超时。

3.使用解题思路中提供的方法,是否已经将所有情况都列举出来了?

 答:没有。解题思路中的方法在使用的时候会发现,第一张牌不管在任何时候,都不会有任何的操作。

 第一张牌的状态可能发生变化,但是第一张牌的另一个属性,翻与不翻从来没有发生过变化。

4.翻牌的顺序对结果是否有影响?

 答:没有。翻牌的顺序不会影响结果的,因为翻每一张牌所产生的反应都是固定的,不会因为牌状态

不同而产生不同的结果。如果已经确定了翻那几张牌,那么不管翻牌的顺序是怎样的,某一张牌变化

 次数是固定的,因此翻牌顺序不会影响牌

的结果。

综上所述:由于第一张牌的翻不翻这个属性始终没有变化,因此只需要枚举第一张牌翻不翻这个状态就可以,也就是枚举两次,就可以得到结果,然后再从这两个结果中找到一个较小的值输出就可以了。使用解题思路中描述的方法,每次进行完一轮操作以后,判断最后一张牌是否是正面,如果是,则说明找到一组解,否则继续找。

下面是代码:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s;
    while(cin>>s)
    {
        bool a[50]={0};
        int T=s.length();
        int k=2;
        int flag=0;
        int sum=0,ans=9999999;
        while(k--)//循环2次,用来枚举第一张牌的翻与不翻两种状态
        {
            sum=0;
            for(int i=0;i<T;i++)//每次枚举的时候都要给a数组进行初始化
                a[i]=s[i]=='1'?true:false;
            if(k==0)//第一次循环不执行此步操作,第二次循环时候执行
            {
                a[0]=!a[0];//翻第一张牌
                if(T>1)
                    a[1]=!a[1];
                sum++;
            }
            for(int i=0;i<T-1;i++)//遍历T-1张牌
            {
                if(a[i]==true)//如果当前这张牌是背面,则需要翻其后面一张牌
                {
                    sum++;
                    a[i]=!a[i];
                    a[i+1]=!a[i+1];
                    if(i+2<T)
                        a[i+2]=!a[i+2];
                }
            }
            if(a[T-1]==false)//如果最后一张牌也是正面,说明这一组牌都已经是正面了
            {
                flag=1;
                ans=ans>sum?sum:ans;
            }
        }
        if(flag==0)
            cout<<"NO"<<endl;
        else
            cout<<ans<<endl;
    }
    return 0;
}







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值