POJ1351 Number of Locks(数学)

截至写博客为止,貌似这是网上第一个采用数学公式来处理的。

网上的题解都是DFS或是动态规划,但感觉可以推公式直接用数学的方法处理,想了好久,终于推出公式。

题意:一个长度为n的由数字1,2,3,4 组成的序列,求至少有一对1,4相邻且2或3必须用上的方法数。

思路: 计A为有1,4相邻的方法数,B为有1,4相邻且无2,3的方法数,则答案为A - B

B很容易求,为2 ^ n - 2 ,再考虑A

计f(n)为有1,4相邻的方法数,g(n)为无1,4相邻但以1,或4开头的方法数

长度为n - 1且有1,4相邻的序列,后面接上1,2,3,4,仍然有1,4相邻,无1,4相邻但以1,4开头前面加上1,4 ,变成有1,4相邻的序列,故有递推公式

f[n] = 4 * f[n - 1 ]  + g[n - 1]

考虑g(n) ,1或4后面是2,3,方法数为2 * 2 *(4 ^(n - 2) - f(n - 2)),或者1或4后面数与他相同,则变成一个字问题,方法数为g(n - 1),故

g(n) =  2 * 2 *(4 ^(n - 2) - f(n - 2)) + g(n - 1)

故可采取递推方式

代码如下:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<string>
 6 #include<algorithm>
 7 #include<map>
 8 #include<queue>
 9 #include<vector>
10 #include<cmath>
11 using namespace std;
12 typedef long long LL;
13 const int N = 1008, INF = 0x3F3F3F3F;
14 LL f[N], g[N];
15 int main(){
16     f[1] = 0;
17     f[2] = 2;
18     g[1] = 2;
19     g[2] = 6;
20     for(int i = 3; i <= 17 ; i++){
21         f[i] = 4 * f[i - 1 ]  + g[i - 1];
22         g[i] = (1 <<(2 * i - 2)) - 4 * f[i - 2] + g[i - 1];
23     }
24     int n;
25     while(~scanf("%d", &n) && n != -1){
26         printf("%d: %I64d ",n, f[n] - (1 << n) + 2);
27 
28     }
29 
30     return 0;

31 } 

 采用数学方法,代码如此简洁

原文地址:https://www.cnblogs.com/IMGavin/p/5645097.html