计数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×m的棋盘上放任意多个炮,使得它们不能相互攻击,求方案数.n,m<=100
前置知识:炮的攻击方式
一开始以为是炮可以攻击同一行和同一列,然而并不是..."马后炮"了解一下?
转化题目:每一行,每一列不能出现超过2个棋子的方案数.
一开始想到了状压,但是状压压不了100位,即使用bitset强行压起来了也没法转移.后来发现具体是哪一行有两个棋子或者一个棋子并不重要,我们只需要知道有多少列有两个、一个、没有棋子就可以转移了,因为每列的本质其实是一样的。dp[i][j][k]表示前i行中j列有一个棋子,k列有两个棋子的方案数,转移比较麻烦.
·如果这一行一个都不放,可以直接继承上面的状态;
·如果放一个,可以放到已有一个的地方,那么这一行数量加一,方案数为(j+1)×dp[i−1][j+1][k−1],也可以放到本来为空的地方dp[i−1][j−1][k]×(m−(j−1)−k);
·如果放两个,可以都放到已有一个的地方dp[i−1][j+2][k−2]×c[j+2][2],也可以都放到空位上dp[i−1][j−2][k]×c[m−(j−2)−k][2],也可以一个放到已有一个的地方,另一个放到空位上dp[i−1][j][k−1]×(m−j−(k−1))×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 }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· 为DeepSeek添加本地知识库
· 精选4款基于.NET开源、功能强大的通讯调试工具
· DeepSeek智能编程
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~