[AHOI2009]中国象棋

[AHOI2009]中国象棋

标签: 动态规划 棋盘型


题目描述

这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!

输入输出格式

输入格式:

一行包含两个整数N,M,之间由一个空格隔开。

输出格式:

总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。

输入输出样例

输入样例#1:
1 3

输出样例#1:
7

题解

分析题目性质可得,每一行每一列的炮不能超过两个。(三个及其以上肯定会攻击到)
因此我们可以枚举每一行,看如何放炮:
放0个,放1个,放两个,以及放的位置。
可知,当我们放炮时,放炮的合法性会受到列的影响(不能超过三个),因此我们需要把每列炮的数量这个状态记录下来。
设f[i][j][k]表示第i行有j列有一个炮,k列有两个炮,m-j-k列没有炮的方案数。
那么转移的话分类讨论即可。

f[i][j][k] = (f[i][j][k] + f[i - 1][j][k]) % p; //不放 
//放一个 
if(j > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - j - k + 1) % p) % p; //放到没有炮的那一列中 
if(j < m && k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1) % p) % p; //放到一个炮的那一列中
//放两个 
if(j > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * C(m - j - k + 2) % p) % p; //两个炮放到只有一个炮的列中 
if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - k + 1) % p) % p; //两个炮分别放到0炮的列中和1炮的列中 
if(j + 2 <= m && k > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * C(j + 2) % p) % p; //两个炮分别放到一炮的那一列中

代码

#include<bits/stdc++.h>
#define N 110
#define p 9999973
#define ll long long
using namespace std;

int n, m;
ll f[N][N][N];

ll C(ll x)
{
    return (x * (x - 1) / 2) % p;
}

int main()
{
    scanf("%d%d", &n, &m);
    f[0][0][0] = 1;
    for(int i = 1; i <= n; i ++)
        for(int j = 0; j <= m; j ++)
            for(int k = 0; k + j <= m; k ++)
            {
                f[i][j][k] = (f[i][j][k] + f[i - 1][j][k]) % p; //不放 
                //放一个 
                if(j > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - j - k + 1) % p) % p; //放到没有炮的那一列中 
                if(j < m && k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1) % p) % p; //放到一个炮的那一列中
                //放两个 
                if(j > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * C(m - j - k + 2) % p) % p; //两个炮放到只有一个炮的列中 
                if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - k + 1) % p) % p; //两个炮分别放到0炮的列中和1炮的列中 
                if(j + 2 <= m && k > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * C(j + 2) % p) % p; //两个炮分别放到一炮的那一列中 
            }
    ll ans = 0;
    for(int j = 0; j <= m; j ++)
        for(int k = 0; k + j <= m; k ++)
            ans += f[n][j][k], ans %= p;
    printf("%lld", ans);
    return 0;
} 

 上一篇
Sum of the Line Sum of the Line
Sum of the Line标签: 莫比乌斯反演 数学 题目描述戳这里大意就是给你一个$n$,求在小于$n$里面与$n$互质的数的平方和是多少,即: $$\sum_{i=1}^{n}i^{2}[gcd(n,i)==1]$$ 题解比赛的时
2019-08-11
下一篇 
[SCOI2007]排列 [SCOI2007]排列
[SCOI2007]排列标签:动态规划 状态压缩 题目描述给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。例如123434有90种排列能被2整除,其中末位为2的有30种,末位为4的有60种。 输入输出格式
2019-07-09