组合数学相关的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同理
代码如下

ContractedBlock.gif ExpandedBlockStart.gif 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教主留下的经典
代码如下
ContractedBlock.gif ExpandedBlockStart.gif 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的嘎嘎,斜率也该复习一下了,都快忘记了

转载于:https://www.cnblogs.com/ronaflx/archive/2011/03/23/1992522.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
矩阵计数是一道经典的组合数学问题,可以用状压DP来解决。以下是一份Python代码的参考实现。 首先,我们需要输入矩阵的行数和列数,以及每行和每列的限制数。然后,我们可以使用二进制数来表示每行和每列的状态,其中1表示该行或该列已经有了一个矩阵,0表示该行或该列还可以放置一个矩阵。 接下来,我们可以使用状压DP来计算矩阵的数量。我们可以定义一个三维数组dp,其中dp[i][j][s]表示在第i行,第j列,状态为s时的矩阵数量。可以通过枚举上一个状态s',来更新dp[i][j][s]。具体来说,如果s'与s在第i行和第j列上的状态都是0,则可以从dp[i][j-1][s']或dp[i-1][j][s']转移而来。如果s'与s在第i行或第j列上的状态不同,则不能转移。最后,dp[m][n][0]就是最终的答案。 下面是完整的代码实现: ```python n, m, k1, k2 = map(int, input().split()) # 行状态用二进制数表示 row_mask = [0] * n for i in range(n): row_mask[i] = int(''.join(input().split()), 2) # 列状态用二进制数表示 col_mask = [0] * m for j in range(m): col_mask[j] = int(''.join(input().split()), 2) # 初始化dp数组 dp = [[[0 for _ in range(1 << m)] for _ in range(m + 1)] for _ in range(n + 1)] dp[0][0][0] = 1 # 状压DP for i in range(1, n + 1): for j in range(m + 1): for s in range(1 << m): for sp in range(1 << m): # 如果s'与s在第i行和第j列上的状态都是0,则可以从dp[i][j-1][s']或dp[i-1][j][s']转移而来 if (sp & s) == 0 and (row_mask[i - 1] & sp) == 0 and (col_mask[j - 1] & sp) == 0: if j == 0: dp[i][1][sp] += dp[i - 1][m][s] else: dp[i][j + 1][sp] += dp[i][j][s] + dp[i - 1][j][s] # 如果s'与s在第i行或第j列上的状态不同,则不能转移 else: continue # 计算答案 ans = 0 for s in range(1 << m): if bin(s).count('1') == k2: ans += dp[n][m][s] print(ans % 998244353) ``` 其中,我们使用了Python内置的bin函数来将一个整数转换为二进制字符串,并使用count方法来计算其中1的个数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值