计数dp

计数dp

  计数类的$dp$没做过几个,所以之前都放到"思维"标签下了,后来发现原来这属于一类问题啊...搬过来了.

  

  管道取珠:https://www.lydsy.com/JudgeOnline/problem.php?id=1566

  题意概述:

  

  有这样的两根管道接在一起,上边有$n$个珠子,下边有$m$个,每次可以从上或下取出一个(向右滑出去),只会有两种颜色的珠子,求不同的取珠序列的总数的平方和.(只要两个取出序列的珠子颜色都相同就视为相同的序列,不用考虑上下).$n,m<=300$

  $shallwe$:完全可以作为$NOIP$ $D2T1$.

  既然要求平方和,是不是要先求每种序列的方案数再平方求和呢?然而这样做是做不出来的.

  出题人怎么说就怎么做是没有前途的...这里有一个挺神奇的转化:视为两个人各拿一个这样的管道取珠,当两个人取到的方案完全相同时答案$+1$,考虑这样做为什么是对的.比如取出某个序列有$a$种方法,那么这第一个人从这$a$种中任取一种,第二个人就有$a$种方法取出与之相同的序列,也就是平方了.太奇妙了.

  这个转移其实非常简单,考虑一个最普通的:$dp[i][j][k][z]$表示第一个人在上边选了$i$个,下面选了$j$个,第二个人在上边选了$k$个,下边选了$z$个的方案数.既然序列要相同,那两个人取的珠子总数应该是相同的,可以直接删掉任意一维.现在时间复杂度已经能过了,但是空间复杂度又过不了了...显然需要用一个滚动数组,但是这样的状态没有办法滚动,所以重新设计:$dp[s][i][k]$,为了方便理解直接沿用了上边的下标名称,表示共取了$s$个,第一个人在上边取了$i$个,第二个人在上边取了$j$个的方案数.此时可以发现$s$每次只会增加$1$,滚动掉.

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <queue>
 4 # include <cstring>
 5 # include <string>
 6 # define R register int
 7 # define ll long long
 8 # define mod 1024523
 9 
10 using namespace std;
11 
12 const int maxn=503;
13 int n,m;
14 char s[maxn];
15 int a[maxn],b[maxn];
16 int dp[3][maxn][maxn];
17 
18 int ad (int a,int b)
19 {
20     a=a+b;
21     if(a>=mod) a-=mod;
22     return a;    
23 }
24 
25 int main()
26 {
27     scanf("%d%d",&n,&m);
28     scanf("%s",s+1);
29     for (R i=n;i>=1;--i)
30         if(s[i]=='A') a[n-i+1]=1;
31     scanf("%s",s+1);
32     for (R i=m;i>=1;--i)
33         if(s[i]=='A') b[m-i+1]=1;
34     dp[0][0][0]=1;
35     for (R s=0;s<=n+m;++s)
36     {
37         int las=s&1;
38         int no=las^1;
39         memset(dp[no],0,sizeof(dp[no]));
40         for (R i=0;i<=n;++i)
41             for (R k=0;k<=n;++k)
42                 {
43                     int j=s-i,z=s-k,x=dp[las][i][k];
44                     if(!x) continue;
45                     if(a[i+1]==a[k+1]) dp[no][i+1][k+1]=ad(dp[no][i+1][k+1],x);
46                     if(a[i+1]==b[z+1]) dp[no][i+1][k]=ad(dp[no][i+1][k],x);
47                     if(b[j+1]==a[k+1]) dp[no][i][k+1]=ad(dp[no][i][k+1],x);
48                     if(b[j+1]==b[z+1]) dp[no][i][k]=ad(dp[no][i][k],x);}
49     }
50     printf("%d",dp[(n+m)&1][n][n]);
51     return 0;
52 }
管道取珠

  中国象棋:https://www.lydsy.com/JudgeOnline/problem.php?id=1801

  题意概述:在一个$n imes m$的棋盘上放任意多个炮,使得它们不能相互攻击,求方案数.$n,m<=100$

  前置知识:炮的攻击方式

  一开始以为是炮可以攻击同一行和同一列,然而并不是..."马后炮"了解一下?

  转化题目:每一行,每一列不能出现超过$2$个棋子的方案数.

  一开始想到了状压,但是状压压不了$100$位,即使用$bitset$强行压起来了也没法转移.后来发现具体是哪一行有两个棋子或者一个棋子并不重要,我们只需要知道有多少列有两个、一个、没有棋子就可以转移了,因为每列的本质其实是一样的。$dp[i][j][k]$表示前$i$行中$j$列有一个棋子,$k$列有两个棋子的方案数,转移比较麻烦.

  ·如果这一行一个都不放,可以直接继承上面的状态;

  ·如果放一个,可以放到已有一个的地方,那么这一行数量加一,方案数为$(j+1) imes dp[i-1][j+1][k-1]$,也可以放到本来为空的地方$dp[i-1][j-1][k] imes (m-(j-1)-k)$;

  ·如果放两个,可以都放到已有一个的地方$dp[i-1][j+2][k-2] imes c[j+2][2]$,也可以都放到空位上$dp[i-1][j-2][k] imes c[m-(j-2)-k][2]$,也可以一个放到已有一个的地方,另一个放到空位上$dp[i-1][j][k-1] imes (m-j-(k-1)) imes j$。($c$数组中是组合数)

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <cstring>
 4 # define ll long long
 5 # define R register int
 6 # define mod 9999973
 7 
 8 using namespace std;
 9 
10 int n,m,ans;
11 long long dp[105][105][105];
12 int c[105][3];
13 
14 int ad (int i,int j,int k)
15 {
16     long long a=0;
17     a=dp[i-1][j][k]%mod;
18     if(j) a=(a+dp[i-1][j-1][k]*(m-(j-1)-k))%mod;
19     if(k) a=(a+(dp[i-1][j+1][k-1]*(j+1)))%mod;
20     if(j>=2) a=(a+(dp[i-1][j-2][k]*c[m-(j-2)-k][2]))%mod;
21     if(k) a=(a+(dp[i-1][j][k-1]*(m-j-(k-1))%mod*j))%mod;
22     if(k>=2) a=(a+(dp[i-1][j+2][k-2]*c[j+2][2]))%mod;
23     return a;
24 }
25 
26 int main()
27 {
28     scanf("%d%d",&n,&m);
29     dp[0][0][0]=1;
30     c[0][0]=1;
31     for (R i=1;i<=m+2;++i)
32     {
33         c[i][0]=1;
34         for (R j=1;j<=2;++j)
35             c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
36     }
37     for (R i=1;i<=n;++i)
38         for (R j=0;j<=m;++j)
39             for (R k=0;k+j<=m;++k)
40                 dp[i][j][k]=ad(i,j,k);
41     for (R i=0;i<=m;++i)
42         for (R j=0;j+i<=m;++j)
43             ans=(ans+dp[n][i][j])%mod;
44     printf("%d",ans);
45     return 0;
46 }
中国象棋
原文地址:https://www.cnblogs.com/shzr/p/9839697.html