中国象棋 题解

中国象棋这道题才看到的时候,畏难情绪很重啊,先介绍题目,大意是NM列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。请问有多少种放置方法。

考试的时候没有其他的想法,就只想暴力骗分,用一个一维的标记数组,再用一个递归,每排最多放两个。后面想来,完全可以把每排最多放两个的情况细化。1.不放 2.放1个 3.放两个。

定义状态f[i][j][k]表示在棋盘的前i行放置炮,使得M列中有1个炮的列数为j,有2个炮的列数为k的方案数。

考虑第i行怎么放,分为以下几种情况:

1、 如果第i行没有放置任何炮,则方案数为:f[i-1][j][k]

2、 如果第i行放置了1个炮

① 如果这个炮所放置的列上的炮数量为0(前i-1行),说明前i-1行炮数为1的列数肯定是j-1,则对应的方案为:f[i-1][j-1][k]*(m-(j-1)-k)

② 如果这个炮所放置的列上的炮数量为1(前i-1行),说明前i-1行炮数为1的列数肯定是j+1,炮数为2的列数肯定是k-1,这样通过在i行选择一个炮数为1的列放置炮,炮数为1的列数会减1,炮数为2的列数会加1,所以对应的方案为:f[i-1][j+1][k-1]*(j+1)

3、如果第i行放置了2个炮

① 如果这2个炮所放置的列上的炮数量为0(前i-1行),说明前i-1行炮数为1的列数肯定是j-2,则对应的方案为:f[i-1][j-2][k]*C(m-(j-2)-k,2)

② 如果这2个炮所放置的列上的炮数量为1(前i-1行),说明前i-1行炮数为1的列数肯定是j+2,炮数为2的列数肯定是k-2,这样通过在i行选择两个个炮数为1的列放置炮,

炮数为1的列数会减2,炮数为2的列数会加2,则对应的方案为:f[i-1][j+2][k-2]*C(j+2,2)

③ 如果两个炮一个放置炮数为0的列(前i-1行),一个放置在炮数为1的列(前i-1行),说明前i-1行炮数为1的列数肯定是j,炮数为2的列数肯定是k-1,答案为f[i-1][j][k-1]*(m-j-(k-1))*j


分析这道题,一定要抓好分类对象是放几个炮,不然就会有诸如f[i-1][j][k-1]*C(m-j,2)的异想天开。

另外一个理解比较容易一些

设f[i][j][k]表示我们要在第i行到n行的棋盘中放置炮,其中前i-1行炮数为1的列数为j,炮数为2的列数为k,在这样的前提下一共有多少放置方案

考虑第i行:

1、 如果放置0个炮,方案数为f[i+1][j][k]

2、 如果放1个炮,考虑炮的位置

①  放在前i行炮数为0的列,方案=f[i+1][j+1][k]*(m-j-k)

② 放在前i行炮数为1的列,方案=f[i+1][j-1][k+1]*j

3、 如果放2个炮,考虑以下情况:

①  放在前i行炮数为0的列,方案=f[i+1][j+2][k]*C(m-j-k,2)

② 放在前i行炮数为1的列,方案=f[i+1][j-2][k+2]*C(j,2)

③ 一个放在炮数为0的列,一个放在炮数为1的列,方案=f[i+1][j][k+1]*(m-j-k)*j

 

这样就可以了,再注意一些细节和边界条件,比前一种好理解*/

一个顺推一个倒推

下面提供顺推的代码

#include<iostream>
using namespace std;
const int mo=9999973;
long long n,m,f[111][111][111];
long long ans=0;
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-1][j][k]+f[i][j][k];
if(j>=1) f[i][j][k]+=(f[i-1][j-1][k]*(m-k-j+1))%mo ;
if(k>=1) f[i][j][k]+=(f[i-1][j+1][k-1]*(j+1)%mo);
if(j>=2) f[i][j][k]+=(f[i-1][j-2][k]*(m-j-k+2)*(m-j-k+1)/2%mo);
if(k>=2) f[i][j][k]+=(f[i-1][j+2][k-2]*(j+2)*(j+1)/2%mo);
if(k>=1) f[i][j][k]+=(f[i-1][j][k-1]*(m-j-k+1)*j%mo);
}
}
}
for(int i=0;i<=m;i++){
for(int j=0;j<=m-i;j++){
ans=ans+f[n][i][j];
ans%=mo;
}
}
printf("%lld",ans);;
return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值