蓝桥杯 Cowboys

问题描述
  一个间不容发的时刻:n个牛仔站立于一个环中,并且每个牛仔都用左轮手枪指着他旁边的人!每个牛仔指着他顺时针或者逆时针方向上的相邻的人。正如很多西部片那样,在这一刻,绳命是入刺的不可惜……对峙的场景每秒都在变化。每秒钟牛仔们都会分析局势,当一对相邻的牛仔发现他们正在互指的时候,就会转过身。一秒内每对这样的牛仔都会转身。所有的转身都同时在一瞬间发生。我们用字母来表示牛仔所指的方向。“A”表示顺时针方向,“B”表示逆时针方向。如此,一个仅含“A”“B”的字符串便用来表示这个由牛仔构成的环。这是由第一个指着顺时针方向的牛仔做出的记录。例如,牛仔环“ABBBABBBA”在一秒后会变成“BABBBABBA”;而牛仔环“BABBA”会变成“ABABB”。 这幅图说明了“BABBA”怎么变成“ABABB” 一秒过去了,现在用字符串s来表示牛仔们的排列。你的任务是求出一秒前有多少种可能的排列。如果某个排列中一个牛仔指向顺时针,而在另一个排列中他指向逆时针,那么这两个排列就是不同的。
输入格式
  输入数据包括一个字符串s,它只含有“A”和“B”。
输出格式
  输出你求出来的一秒前的可能排列数。
数据规模和约定
  s的长度为3到100(包含3和100)
样例输入
BABBBABBA
样例输出
2
样例输入
ABABB
样例输出
2
样例输入
ABABAB
样例输出
4
样例说明
  测试样例一中,可能的初始排列为:"ABBBABBAB"和 "ABBBABBBA"。

  测试样例二中,可能的初始排列为:"AABBB"和"BABBA"。


题解:AB->BA

解题思路是dp,通过dp[position][0]与dp[position][1]分别记路position位置不经变化或经变化得到。

转移方程:1.当前与前邻相同时,不可能有反转而得。dp[now][0] = dp[pre][0]+dp[pre][1],dp[now][1] = 0;

2.当前为'B',前邻为'A',不可能由翻转得到(翻转得到的唯一状态为'BA'),但'AB'为不稳定状态,在当前‘B’看来,'A'一定经过翻转(实际不一定,在'B'的后邻会弥补)dp[now][0] = dp[pre][1]; dp[now][1] = 0;

3.当前为'A',前邻为'B',dp[now][0] = dp[pre][0]; dp[now][1] = dp[ppre][0] + dp[ppre][1];


大致思路明了,首要问题是要找一个稳定状态(即确定变化可能情况的位置)来确定start与enda位置。'AA'或'BB'是稳定的,当不具有普遍性,比如'ABABAB'不存在'AA'或'BB'。因此取'BA'位置,当不存在'BA'时,那么它必然是纯A或纯B串,答案就是1。

否则将一次'BA'出现位置找出来,记B位置为pos,start = (pos+2)%len, enda = (pos-1+len)%len。

初始化dp[start][0] = 1, dp[start][1] = 0。sum = dp[enda][0]+dp[enda][1]。这是默认该‘BA’是反转而来的所得结果。这种情况下不必考虑'BA'前邻与后邻的情况,因为反转之前'AB'与前邻和后邻不具有不稳定状态的可能性。

其次,考虑'BA'未经反转,一直如次,这种情况在前邻'AA'或后邻'BB'的情况下是不成立的。因此前邻是'AA'或后邻是'BB'时,上述sum就是最终结果。

当前邻是'B'&&后邻是'A'时,未经反转是可能的,求解与翻转时相同。sum+未翻转时情况为最终解。

当前邻是'BA'或后邻是'BA'时,比如说前邻是'BA',那么由于这段前邻一定不是反转而来的,它失去了dp性,因此将enda后退两格,诸如此。


#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;
char s[101];
int len,start,enda;
ll dp[100][2];

int main()
{
    while(scanf("%s",s)!=EOF)
    {
        len = strlen(s);
        bool mark = true;
        for(int i = 1; i<len; i++)
        {
            if(s[i] != s[(i-1+len)%len])
            {
                mark = false;
                break;
            }
        }
        if(mark)
        {
            printf("1\n");
            continue;
        }
        for(int i = 0; i<len; i++)
        {
            int pre = (i-1+len)%len;
            if(s[i] == 'A' && s[pre] == 'B')
            {
                start = (i+1)%len;
                enda = (pre-1+len)%len;
                break;
            }
        }
        memset(dp,0,sizeof(dp));
        dp[start][0] = 1;
        dp[start][1] = 0;
        for(int i = start; ; )
        {
            if(i%len == enda)
                break;
            i++;
            int now = i%len;
            int pre = (i-1)%len;
            if(s[now] == s[pre])
            {
                dp[now][0] = dp[pre][0] + dp[pre][1];
                dp[now][1] = 0;
            }
            else if(s[now] == 'B')
            {
                dp[now][0] = dp[pre][1];
                dp[now][1] = 0;
            }
            else
            {
                dp[now][0] = dp[pre][0];
                dp[now][1] = dp[(pre-1+len)%len][0] + dp[(pre-1+len)%len][1];
                if(dp[now][1] == 0)
                    dp[now][1] = 1;
            }
        }
        ll sum = dp[enda][0] + dp[enda][1];
        if((s[enda] == 'A' && s[(enda-1+len)%len] == 'A') || (s[start] == 'B' && s[(start+1)%len] == 'B'))
        {
            printf("%I64d\n",sum);
            continue;
        }
        if(s[enda] == 'A' && s[(enda-1+len)%len] == 'B')
            enda = (enda-1+len)%len;
        if(s[start] == 'B' && s[(start+1)%len] == 'A')
            start = (start+2)%len;
        if((start-1+len)%len == enda)
        {
            printf("I64d\n",sum+1);
            continue;
        }
        memset(dp,0,sizeof(dp));
        dp[start][0] = 1;
        dp[start][1] = 0;
         for(int i = start; i!=enda ; )
        {
            if(i%len == enda)
                break;
            i++;
            int now = i%len;
            int pre = (i-1)%len;
            if(s[now] == s[pre])
            {
                dp[now][0] = dp[pre][0] + dp[pre][1];
                dp[now][1] = 0;
            }
            else if(s[now] == 'B')
            {
                dp[now][0] = dp[pre][1];
                dp[now][1] = 0;
            }
            else
            {
                dp[now][0] = dp[pre][0];
                dp[now][1] = dp[(pre-1+len)%len][0] + dp[(pre-1+len)%len][1];
                if(dp[now][1] == 0)
                    dp[now][1] = 1;
            }
        }
        sum+=(dp[enda][0]+dp[enda][1]);
        printf("%I64d\n",sum);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值