Luogu P2051 [AHOI2009]中国象棋 //DP
Posted sinxiii
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P2051 [AHOI2009]中国象棋 //DP相关的知识,希望对你有一定的参考价值。
hhy给我推的这道题,我不会,我去康了题解.....
题解第一个人说自己20min K一道省选DP,还一次AC
我想着哪个人这么强,往上一翻发现是__stdcall哥哥......也许这就是大佬的世界吧
50pts:
首先可以想到的是状压DP,因为一行一列要最多不超过2个棋子,
所以用3进制表示整个棋盘每列的放了几个,dp[棋盘状态],边界为dp[0]=1
刷表法比填表法好写DP方程,转移时,在0或者1的位置上加1构成新的棋盘状态,
最后统计所有棋盘状态就行了。
100pts:
在50分的DP中我们反思,什么地方浪费了空间?我们全部状压,存了每条列的顺序和状态
但是在这道题中顺序对答案不造成影响,于是我们可以只存在这个状态的列有多少条,而忽略顺序
于是正确的DP状态就来了,dp[摆到x行][有i列摆了1个][有j列摆了2个],
刷表转移,加法原理转移到下一个状态,乘法原理把当前状态和放法相乘,
最后统计答案
刚开始写填表写挂了,然后写刷表,刷表写完了改了填表
刷表法:
#include<bits/stdc++.h> #define mod 9999973 using namespace std ; const int MAXN = 110,MAXM = 110; long long dp[MAXN][MAXM][MAXM],n,m; int main() cin>>n>>m; dp[0][0][0]=1; for(int x=0;x<n;x++) for(int i=0;i<=m;i++) for(int j=0;i+j<=m;j++) if(dp[x][i][j]) (dp[x+1][i][j] += dp[x][i][j]) %= mod; if(m-i-j>0)(dp[x+1][i+1][j] += dp[x][i][j] * (m-i-j)) %= mod; if(i>=1)(dp[x+1][i-1][j+1] += dp[x][i][j] * i) %= mod; if(m-i-j-1>0)(dp[x+1][i+2][j] += dp[x][i][j] * (m-i-j) * (m-i-j-1) / 2) %= mod; if(m-i-j>=0 && i>=1)(dp[x+1][i][j+1] += dp[x][i][j] * (m-i-j) * i) %= mod; if(i>=2)(dp[x+1][i-2][j+2] += dp[x][i][j] * (i-1) * i / 2) %= mod; long long ans = 0; for(int i=0;i<=m;i++) for(int j=0;j<=m;j++) if(i+j<=m) ans += dp[n][i][j]; ans %= mod; cout<<ans<<endl; return 0;
填表法:
#include<bits/stdc++.h> #define mod 9999973 using namespace std ; const int MAXN = 110,MAXM = 110; long long dp[MAXN][MAXM][MAXM],n,m; int main() cin>>n>>m; dp[0][0][0]=1; for(int x=1;x<=n;x++) for(int i=0;i<=m;i++) for(int j=0;i+j<=m;j++) dp[x][i][j] += dp[x-1][i][j],dp[x][i][j] %= mod;//什么也不做 if(i>=1) dp[x][i][j] += dp[x-1][i-1][j] * (m-i-j+1),dp[x][i][j] %= mod;//放一个在一列0上; if(j>=1) dp[x][i][j] += dp[x-1][i+1][j-1] * (i+1),dp[x][i][j] %= mod;//放一个在一列1上; if(i>=2) dp[x][i][j] += dp[x-1][i-2][j] * (m-i-j+2) * (m-i-j+1) / 2,dp[x][i][j] %= mod;//放两个在2列0上 if(j>=2) dp[x][i][j] += dp[x-1][i+2][j-2] * (i+2) * (i+1) / 2,dp[x][i][j] %= mod;//放两个在2列1上 if(j>=1 && i>=1) dp[x][i][j] += dp[x-1][i][j-1] * (m-i-j+1) * i,dp[x][i][j] %= mod;//放2个,一个在1,一个在2上 long long ans = 0; for(int i=0;i<=m;i++) for(int j=0;i+j<=m;j++) ans += dp[n][i][j]; ans %= mod; cout<<ans<<endl; return 0;
状态设计是一个巧活,状态是获得答案的最简条件时,这个DP才是最好的。
TAG:SIN_XIII ⑨
以上是关于Luogu P2051 [AHOI2009]中国象棋 //DP的主要内容,如果未能解决你的问题,请参考以下文章
luogu P2051 [AHOI2009]中国象棋 dp 状态压缩+容斥