组合数学相关的DP

鉴于本人接近于0的数学能力,所以所能学到的东西有限,写点东西留着复习用。希望写的这点东西不要侮辱了数学%%……*%&

本着学习的态度找来了一些组合数学相关的DP题,至于其他所谓的数学题就不在我的研究范围内了……队友呀,靠你们了

组合数学的DP题,有这样一种带有状态压缩的思想,状态转移方程往往非常巧妙
最近学习了一些,由于个人数学能力有限,所以只能做一些小小的总结,以后复习用
HOJ 2595
用4个bit标记状态
那么只有6个合法的状态 0000, 0011, 0110,1001,1100, 1111
分别标记为0 1 2 3 4 5
dp[i][0] = dp[i - 1][5];
dp[i][1] = dp[i - 1][5] + dp[i - 1][4];
dp[i][2] = dp[i - 1][5] + dp[i - 1][3];
dp[i][3] = dp[i - 1][2];
dp[i][4] = dp[i - 1][1] + dp[i - 1][5];
dp[i][5] = dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][4] + dp[i - 1][5];
对于0很显然只有i - 1满的时候是他的前一个状态(不要加上其他的如dp[i - 1][1],dp[i - 1][3] ,dp[i - 1][4], 因为这些状态到dp[i][0]都是化为dp[i - 1][5]得到的)
转移的时候有一个重要的原则如上面所说的,就是不要有状态重复!某个状态的到他下一个状态的过程,一定不要转化到其他的状态!!
最重要的例子如是5的转移,很容易写重复。例如说在dp[i - 1][0] 前面乘一个系数,实际上dp[i - 1][0] 为了保证状态的不叠加必须只能把所有的股牌横着放,其他的同理可以分析。
HOJ 2894
一个简化版的股牌的覆盖问题。
f[i] 表示长度为i个覆盖有多少个
g[i] 表示长度为i的不齐的覆盖,一个比另一个大1
f[i] = f[i - 1] + f[i - 2] + 2 * g[i - 1];
g[i] = g[i - 1] + f[i - 2];
注意只加一个f[i - 2] 因为全是竖着的情况是f[i - 1]的一种。
g[i - 1] * 2是因为有上比下大1和小1两种情况
HOJ 2930
把上面g[i]式子展开带入f[i]
再用f[i] - f[i - 1] = f[i - 1] + f[i - 3];
f[i] = 2 * f[i - 1] + f[i - 3];
同样是一个一样的的DP,单数数据范围巨大,需要用矩阵乘法解决。
HOJ 2124同理
代码如下

HOJ 2124
   
     
1 #include < iostream >
2   using namespace std;
3
4 int main()
5 {
6 int f[ 31 ] = { 1 , 0 , 3 };
7 int x[ 31 ] = { 0 , 0 , 0 }; // 记长的为i
8 for ( int i = 3 ;i < 31 ;i ++ )
9 {
10 if (i % 2 == 1 )
11 f[i] = 0 ;
12 else
13 {
14 f[i] = f[i - 2 ] * 3 ;
15 // 每偶数个格都可以摆成不可分割的的2种形式!!!
16 // 把偶数个查分成若干个2,每个为一个单位,相邻的2个单位,第一个的右边的与第二个左边的横放就变的不可分了
17 int t = i;
18 while (t >= 4 )
19 {
20 f[i] += f[i - t] * 2 ;
21 t -= 2 ;
22 }
23 }
24 }
25 int n;
26 while (scanf( " %d " , & n) == 1 )
27 {
28 if (n < 0 )
29 break ;
30 printf( " %d\n " ,f[n]);
31 }
32 return 0 ;
33 }

此题有一个加强版的题目
HOJ1472
有一份sdfond学长的解题报告,但是没有看懂……我的思路是参考http://godfrey90.javaeye.com/blog/723732这个阶梯报告的
http://www.cppblog.com/sdfond/archive/2009/07/31/91761.html sdfond学长的解题报告
还有一些组合数学的DP有待研究
HOJ 2468也是组合数学的DP,数的划分问题这一个经典的问题。most leg教主留下的经典
代码如下
HOJ 2468
   
     
1 #include < cstdio >
2 #include < cstring >
3 #include < iostream >
4 using namespace std;
5 const int MAXN = 100001 ;
6 const int MAXM = 101 ;
7 const int MOD = 10000 ;
8 int dp[MAXN][MAXM];
9 int main()
10 {
11 memset(dp, 0 , sizeof (dp));
12 for ( int i = 0 ;i < MAXM;i ++ )
13 dp[ 0 ][i] = 1 ;
14 for ( int i = 1 ;i < MAXN;i ++ )
15 {
16 for ( int j = 1 ;j < MAXM;j ++ )
17 {
18 dp[i][j] = dp[i][j - 1 ];
19 if (i >= j) dp[i][j] += dp[i - j][j];
20 dp[i][j] %= MOD;
21 }
22 }
23 int n, m, t;
24 scanf( " %d " , & t);
25 while (t -- )
26 {
27 scanf( " %d %d " , & n, & m);
28 m = m - (n + 1 + 2 * n) * n / 2 ;
29 if (m >= 0 )
30 printf( " %d\n " ,dp[m][n]);
31 else
32 printf( " 0\n " );
33 }
34 return 0 ;
35 }

PS:突然发现,写一些比较热的内容能赚访问量也那篇斜率优化已经被访问了很多次了……有时间写个四边形不等式和AC自动机DP的嘎嘎,斜率也该复习一下了,都快忘记了

你可能感兴趣的:(dp)