"红色病毒"问题 HDU 2065 递推+找循环节
题目连接:
http://acm.hdu.edu.cn/showproblem.php?pid=2065
递推类题目, 可以考虑用数学方法来做, 但是明显也可以有递推思维来理解。
递推的话基本就是状态转移了, 如何找状态是递推的关键。
我们把这个分为四个状态
A 出现次数的奇偶和B出现状态的奇偶,我们可以构造出四个状态:
A B
第一个状态 : 偶 偶 0
第二个状态 : 偶 奇 1
第三个状态 : 奇 偶 2
第四个状态 : 奇 奇 3
我们由这些状态进行转移 设定数组 dp[第k个状态][字符串的长度n]
进行状态转移:
偶偶 <- 偶 奇 + 偶偶 + 奇偶
依次类推........
dp[0][i] = (dp[0][i-1]*2 + dp[1][i-1] + dp[2][i-1]);
dp[1][i] = (dp[1][i-1]*2 + dp[0][i-1] + dp[3][i-1]);
dp[2][i] = (dp[2][i-1]*2 + dp[0][i-1] + dp[3][i-1]) ;
dp[3][i] = (dp[3][i-1]*2 + dp[1][i-1] + dp[2][i-1]) ;
得到四个状态转移方程
dp[0][i-1]*2 是因为 无论是 第i个 字符是A 或者是 B 对下一个状态都 无影响。
他们给我们的数字是非常大的, 因此我们要去找循环节。
循环节是20个数字 一个循环, 但是前两个数字不在循环内部 要特殊处理
下面是代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<stack> 7 #include<string> 8 #include<queue> 9 using namespace std; 10 const int maxn = 50; 11 const long long INF = 0xfffffff; 12 13 int main() 14 { 15 int dp[4][maxn] ={0}; 16 int P[maxn]; 17 dp[0][0] = 1; 18 for(int i=1; i<=30; i++) 19 { 20 dp[0][i] = (dp[0][i-1]*2 + dp[1][i-1] + dp[2][i-1]) % 100; 21 dp[1][i] = (dp[1][i-1]*2 + dp[0][i-1] + dp[3][i-1]) % 100; 22 dp[2][i] = (dp[2][i-1]*2 + dp[0][i-1] + dp[3][i-1]) % 100; 23 dp[3][i] = (dp[3][i-1]*2 + dp[1][i-1] + dp[2][i-1]) % 100; 24 // cout << dp[0][i] << endl; 25 if(i > 2) 26 P[i-3] =dp[0][i]; 27 } 28 int T, cas; 29 30 long long n; 31 while(cin >> T, T) 32 { 33 cas = 1; 34 while(T--) 35 { 36 cin >> n; 37 cout << "Case "<<cas++<<": "; 38 39 if(n < 3) 40 cout<<dp[0][n]<<endl; 41 else 42 cout<<P[(n-3)%20]<<endl; 43 44 if(!T) 45 cout << endl; 46 } 47 } 48 return 0; 49 }