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 ⑨