[AHOI2009]中国象棋

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

1 3

输出样例#1:

7

说明

样例说明

除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有222-1=7种方案。

数据范围

100%的数据中N和M均不超过100

50%的数据中N和M至少有一个数不超过8

30%的数据中N和M均不超过6

一开始看以为是状压

但数据范围显然不支持状压==

膜题解后发现棋子的放置顺序对答案煤油影响

所以只需要考虑放0,1,2枚棋子的有几列而不需要考虑其具体位置

f[i][j][k] 表示前i行有j列有1个棋子,k列有2个棋子

所以有m - j - k列放了0个棋子

假设这一行煤油放棋子

暴力转移

假设这一行放1枚棋子

可以使有0个棋子的列变为1个

可以使有1个棋子的列变为2个

如果这一行放2枚棋子

可以使两个有1个棋子的列变成2个.

也可以使两个有0个棋子的列变成1个 .

也可以使一个有0个棋子的列和一个有1个棋子的列变成一个有1个棋子的列和一个有2个棋子的列

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
# define LL long long
const int M = 105 ;
const int mod = 9999973 ;
using namespace std ;
int n , m ;
LL f[M][M][M] , Ans ; 
inline int C(int n , int m) {
    return n * (n - 1) / 2 ;
} 
int main() {
    cin >> 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 <= m - j ; k ++) {
                f[i][j][k] = (f[i][j][k] + f[i - 1][j][k])%mod ;
                if(j > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - j - k + 1))%mod ; 
                if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1))%mod ;    
                if(j > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * C(m - j - k + 2 , 2))%mod ;
                if(k > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * C(j + 2 , 2))%mod ;
                if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - k + 1))%mod ;
            }
    }
    for(int i = 0 ; i <= m ; i ++)
        for(int j = 0 ; j <= m ; j ++)
            Ans = (Ans + f[n][i][j])%mod ;
    cout << Ans << endl ;
    return 0 ;
}

转载于:https://www.cnblogs.com/beretty/p/9481184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值