杭电 2065 红病毒问题

 这是在学习递推的过程中遇到的问题, 问题描述:

"红色病毒"问题

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2418    Accepted Submission(s): 1033


Problem Description
医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶均是成对出现的。
现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.
 

 

Input
每组输入的第一行是一个整数T,表示测试实例的个数,下面是T行数据,每行一个整数N(1<=N<2^64),当T=0时结束.
 

 

Output
对于每个测试实例,输出字符串个数的最后两位,每组输出后跟一个空行.
 

 

Sample Input
4 1 4 20 11 3 14 24 6 0
 

 

Sample Output
Case 1: 2 Case 2: 72 Case 3: 32 Case 4: 0 Case 1: 56 Case 2: 72 Case 3: 56
 

 

Author
Rabbit
 

 

Source
 

 

Recommend
lcy
 
 
递推方法分析:
  这一题, 同错排问题有很大的相似之处, 我们站在最后一个字母放置的立场上去看待这个问题。 当我们放置最后一个的时候, 设放置方法为f(n, 0), 那么前一次放置的情况有4中, 分别为:
  1.  A,B均为偶数个数    用方程表示f(n-1, 0)
  2.  只有A为偶数个数         f(n-1, 1)
  3.  只有B为偶数个数         f(n-1, 2)
  4.  A,B均不为偶数个数       f(n-1, 3)
则有如下的关系
  1.  f(n, 0) = 2*f(n-1, 0) + f(n-1, 1) + f(n-1, 2);
  2.  f(n, 1) = f(n-1, 0) + 2*f(n-1, 1) + f(n-1,3)
  3.  f(n, 2) = f(n-1, 0) + 2*f(n-1, 2) + f(n-1, 3)
  4.  f(n, 3) = f(n-1, 1) + f(n-1, 2) + 2*f(n-1, 3)
这个关系式的具体含义, 请自己领悟。。。。。
得出这个关系式后, 我们会想到对其进行化简, 其实, 我在解决这一个问题的时候, 并没有对其化简, 只是用程序模拟这种关系, 然后, 找出循环的长度, 最后, 将循环的一个环节, 放在一个长度为25的数组之中。 具体见下面的代码:
  下面这一个程序是为了将f(n,0)计算出来,然后,放进数组里面去。
#include <iostream>
#include <cmath>
#include <iomanip>
#include <fstream>
using namespace std;

int num[26] = {0, 2};
bool isEqual(int a, int b, int l)
{
    for(int i = 1; i <= l; i++)
    {
        if(a == num[i-1] && b == num[i])
            return true;
    }
    return false;
}
int main()
{
    ifstream cin("2065.txt");
    int T;
    __int64 N;
    int a1 , a2 = 2, b1 , b2 = 1, c1 , c2 = 1, d1 , d2 = 0, i;
    a1 = a2; b1 = b2; c1 = c2; d1 = d2;
    for(i = 2; i <=300; i++)
    {
        d2 = b1 + c1 + 2*d1;
        c2 = a1 + 2*c1 + d1;
        b2 = a1 + 2*b1 + d1;
        a2 = 2*a1 + b1 + c1;
        num[i] = a2%100;
        /*if(isEqual(a1, a2, i-1))
        {
            break;
        }*/
        a1 = a2%100;
        b1 = b2%100;
        c1 = c2%100;
        d1 = d2%100;
    }
    while(cin >> T && T)
    {
        for(int j = 1; j <= T; j++)
        {
            cin >> N;
            if(N >=23)
                cout << "Case " << j << ": " << num[N%23 + 3] << endl;
            else
                cout << "Case " << j << ": " << num[N] << endl;
        }
        cout << endl;
    }
    return 0;
}
 
通过观察这个数组,发现一个循环,那么,可以优化程序如下:
#include <iostream>
#include <cmath>
#include <iomanip>
#include <fstream>
using namespace std;
int main()
{
    ifstream cin("2065.txt");
    int T;
    __int64 N;
    int num[26] = {0, 2, 6, 20, 72, 72, 56, 60, 12, 92, 56, 0, 52, 12, 56, 40, 92, 32, 56, 80, 32, 52, 56, 20, 72, 72};

    while(cin >> T ,T)
    {
        for(int j = 1; j <= T; j++)
        {
            cin >> N;
            if(N < 23)
                cout << "Case " << j << ": " << num[N] << endl;
            else
                cout << "Case " << j << ": " << num[(N-3)%20+3] << endl;
        }
        cout << endl;
    }
    return 0;
}
 
很类似网上的代码,其实,网上的代码都是优化后的结果,这也是在AC中要注意的问题。(尽量使用优化后的结果)。
posted @ 2012-08-01 11:39  黑色包裹  阅读(686)  评论(0)    收藏  举报