【题解】AHOI2009中国象棋

  还记得第一次看见这题的时候好像还是联赛前后的事了,那时感觉这题好强……其实现在看来蛮简单的,分类讨论一下即可。题意非常的简单:每一行,每一列都不能超过两个棋子。考虑我们的dp,如果一行一行转移的话行上不能超过两个棋子是很好满足的,就看列上如何满足了。所以状态自然而然的设置为 \(f[i][j][k]\),分别代表枚举到第 \(i\) 行,之前的列上有 \(j\) 列上有两个棋子,\(k\)列上有一个棋子时的方案数。

  然后分情况转移乘以组合数即可。虽然简单,但感觉还是有所启发:做组合数类型的DP,应该观察到对后续状态真正产生影响的要素,只需保留这几样作为状态即可,其余的任选都有组合数来体现(其实一般的DP也是这样吧(`・ω・´))代码里面有小小注释……个人喜欢打英文的(主要是懒得切输入法)。

#include <bits/stdc++.h>
using namespace std;
#define maxn 200
#define int long long
#define mod 9999973
int n, m, ans, f[maxn][maxn][maxn];
int C[maxn][3];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

void up(int &x, int y) { x = (x + y % mod) % mod; }

void init()
{
    for(int i = 0; i <= m; i ++) C[i][0] = 1;
    for(int i = 1; i <= m; i ++)
        for(int j = 1; j <= 2; j ++)
            if(j > i) C[i][j] = 0;
            else C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}

signed main()
{
    n = read(), m = read();
    f[0][0][0] = 1; init();
    for(int i = 0; i < n; i ++)
        for(int j = 0; j <= m; j ++)
            for(int k = 0; k <= m - j; k ++)
            {
                if(!f[i][j][k]) continue;
                up(f[i + 1][j][k], f[i][j][k]); // 0
                int t = m - j - k, S = f[i][j][k];
                // place a chess 
                if(t) up(f[i + 1][j][k + 1], S * t); // placed on a row of 0; 
                if(k) up(f[i + 1][j + 1][k - 1], S * k); // placed on a row of 1;
                // place two chess
                if(t >= 2) up(f[i + 1][j][k + 2], S * C[t][2]); //two on zero;
                if(k && t) up(f[i + 1][j + 1][k], S * t * k); // one on zero, one on one;
                if(k >= 2) up(f[i + 1][j + 2][k - 2], S * C[k][2]); // two on one;
            }
    for(int j = 0; j <= m; j ++)
        for(int k = 0; k <= m - j; k ++)
            up(ans, f[n][j][k]);
    printf("%lld\n", ans);
    return 0;    
} 

 

转载于:https://www.cnblogs.com/twilight-sx/p/9064545.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值