bzoj1801[Ahoi2009]chess 中国象棋 dp
题目描述
在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.
输入格式
一行包含两个整数N,M,中间用空格分开.
输出格式
输出所有的方案数,由于值比较大,输出其mod 9999973
题解
据说三进制只有50分?(其实是我不会三进制)感觉这题就跟状压没关系了,直接dp就可以了。
\(f[i][j][k]\)表示第i行有j列有1个炮,有k列有2个炮。然后转移即可。情况蛮多的,总是写不全qwq。
- 在没有炮的一列放一个
- 在没有炮的两列各放一个
- 在有一个炮的一列再放一个
- 在有一个炮的两列各再放一个
- 在有一个炮的一列再放一个,在没有炮的一列放一个
利用组合数求解。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=9999973;
const int maxn=(1<<9)+10;
typedef long long ll;
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m;
ll inv[110],fac[110];
ll f[110][110][110];
ll pow(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b/=2;
}
return res;
}
ll c(ll n,ll m){
if(n>m)return 0;
return fac[m]*inv[n]%mod*inv[m-n]%mod;
}
void init(){
fac[0]=1;
for(int i=1;i<=101;i++)fac[i]=fac[i-1]*i%mod;
inv[101]=pow(fac[101],mod-2);
for(int i=100;i>=0;i--)inv[i]=(inv[i+1]*(i+1))%mod;
}
int main(){
init();
n=read();m=read();
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;k++){
if(f[i-1][j][k]){
f[i][j][k]=(f[i][j][k]+f[i-1][j][k])%mod;
if(j+k<m)f[i][j+1][k]=(f[i][j+1][k]+f[i-1][j][k]*(m-j-k)%mod)%mod;
if(j+k<m-1)f[i][j+2][k]=(f[i][j+2][k]+f[i-1][j][k]*c(2,m-j-k)%mod)%mod;
if(j>0&&(k+1)<=m)f[i][j-1][k+1]=(f[i][j-1][k+1]+f[i-1][j][k]*j%mod)%mod;
if(j>1&&(k+2)<=m)f[i][j-2][k+2]=(f[i][j-2][k+2]+f[i-1][j][k]*c(2,j)%mod)%mod;
if(j>0&&(j+k)<m)f[i][j][k+1]=(f[i][j][k+1]+f[i-1][j][k]*(m-j-k)%mod*j%mod)%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;
printf("%d\n",ans);
return 0;
}