[AHOI2009]中国象棋

旁边的dalao们,神们,都在做什么树套树平衡树LCT仙人掌模拟退火AC自动机……只有笔者这个蒟蒻弱弱的在写DP。这道Dp还是很有技术含量的。。

先分析一下题目,简单来说就是给一个矩阵,要求再里面放棋子,每行每列的棋子要<=2,问有多少种不同的放置方法。首先分析小数据,n,m均<=6,爆搜即可出解。然后那50%估计是有什么nm^3的做法,然而笔者太弱了没想出来。那么接着分析100%。

考虑到这个题就给了你行和列,所以我们以 行 为大方向考虑动归方程。思考,这么一来我们每行最多只能放两个棋子,那么如果我们以f[i][j][k]来表示到第i行已经有j列放了一个棋子,k列放了两个棋子的方法数量,那么DP完成后我们的答案就是sum(f[n][1...m][1...m])。考虑如何转移:

  首先,我们在下一行不放棋子,那么直接转移这一行的方案数就可以了。

  其次,我们考虑在下一行放一个棋子,那么必须要分情况讨论:

  ①我们在当前已经有一个棋子的一列在放一个棋子,那么状态变为f[i+1][j-1][k+1],而我们可以这么做的方案数就是已经放了j列一个棋子的j。

  ②我们从找一列一个棋子都没有的,放一个棋子,那么状态变为f[i+1][j+1][k],我们能这么做的次数就是当前没有放旗子的列数(m-j-k)。

  最后我们考虑在下一行放两个棋子,仍然需要分情况讨论,

  ①我们把这两个棋子全都放到已经有棋子的列,那么状态变为f[i+1][j-2][k+2],我们可行的不同的方案数就是从j个里面选出两个的数量也就是Cj2

  ②我们放一个棋子在已经有棋子的一列,而另一个放在没有棋子的一列,那么状态变为f[i+1][j][k+1],可行的不同方案数的个数是可以放有一个棋子的j和一个棋子都没有的(m-j-k)的乘积。

  ③我们把两个棋子都放在原来没有棋子的列上,那么状态变为f[i+1][j+2][k],我们可行的方案数是。。

适当的%有益于身心,代码确实不长,但是确实是一道DP好题。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #define inf 5000001
 8 #define re register
 9 #define ll long long
10 #define min(a,b) a<b?a:b 
11 #define maxn 5000007
12 #define mo 9999973
13 using namespace std;
14 ll f[201][201][201],n,k,m;
15 inline ll C(int x)
16 {
17     return x*(x-1)/2;
18 }
19 int main()
20 {
21     cin>>n>>m;
22     f[0][0][0]=1;
23     for(re int i=0;i<n;i++)
24      for(re int j=0;j<=m;j++)
25       for(re int l=0;l+j<=m;l++){
26           if(f[i][j][k]){
27               f[i+1][j][l]=(f[i+1][j][l]+f[i][j][l])%mo;
28               
29             if(m-j-l>=1) f[i+1][j+1][l]=(f[i+1][j+1][l]+f[i][j][l]*(m-j-l))%mo;
30               if(j>=1) f[i+1][j-1][l+1]=(f[i+1][j-1][l+1]+f[i][j][l]*(j))%mo;
31               
32             if(m-j-l>=2) f[i+1][j+2][l]=(f[i+1][j+2][l]+f[i][j][l]*C(m-j-l))%mo;
33               if(m-j-l>=1&&j>=1) f[i+1][j][l+1]=(f[i+1][j][l+1]+f[i][j][l]*(m-j-l)*j)%mo;
34               if(j>=2) f[i+1][j-2][l+2]=(f[i+1][j-2][l+2]+f[i][j][l]*C(j))%mo;
35           
36           }
37       }
38     ll ans=0;
39     for(re int i=0;i<=m;i++)
40      for(re int j=0;j<=m;j++)
41      ans=(ans+f[n][i][j])%mo;
42     cout<<ans;
43 }
原文地址:https://www.cnblogs.com/victorique/p/8619223.html