https://www.luogu.org/problemnew/show/P2051
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。
输出格式:
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
思路:
首先,明确一点:每行/列最多只能放2个炮,3个及以上一定会相互攻击。
一行一行来放,假如当前行放在i,j两列,则需要知道这两列是不是已经放了2个了,如果换两列k,l来放,有需要知道k,l两列是不是已经放了2个了,这样子很难去记录。
最后可耻地又去看了题解。洛谷排名第一的是__stdcall的题解,讲的很好:
棋子的顺序是无所谓的,并不需要准确知道当前棋盘的状态,
需要知道的仅仅是前面的所有行中,{放了2个炮的列有j列,放了1个炮的列有k列},每一个这样子的状态有多少种情况。
然后在当前行中分类讨论怎么放,用加法原理和乘法原理转移。
这里面完全是组合数学的思想。
#include<bits/stdc++.h>
using namespace std;
#define C2(x) ((x)*((x)-1)/2)
#define C1(x) (x)
#define mod 9999973
#define ll long long
int n,m,f[105][105][105];
int main()
{
// freopen("input.in","r",stdin);
cin>>n>>m;
f[0][0][0]=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<=m;j++)
{
for(int k=0;k<=m;k++)
{ /*这行不放*/
f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%mod;
/*放1个*/
if(j+1<=m&&k-1>=0)f[i+1][j+1][k-1]=(f[i+1][j+1][k-1]+(ll)f[i][j][k]*C1(k))%mod;
if(k+1<=m)f[i+1][j][k+1]=(f[i+1][j][k+1]+(ll)f[i][j][k]*C1(m-j-k))%mod;
/*放两个*/
if(k-2>=0)f[i+1][j+2][k-2]=(f[i+1][j+2][k-2]+(ll)f[i][j][k]*C2(k))%mod;
if(k+2<=m)f[i+1][j][k+2]=(f[i+1][j][k+2]+(ll)f[i][j][k]*C2(m-j-k))%mod;
if(j+1<=m)f[i+1][j+1][k]=(f[i+1][j+1][k]+(ll)f[i][j][k]*C1(k)*C1(m-j-k))%mod;
}
}
}
int ans=0;
for(int i=0;i<=m;i++)for(int j=0;j<=m;j++)ans=(ans+f[n][i][j])%mod;
cout<<ans<<endl;
}