[DP]中国象棋

题目描述

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

输入输出格式
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。

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

输入输出样例
输入样例#1: 复制
1 3
输出样例#1: 复制
7
说明
样例说明

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

数据范围

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

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

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

分析

首先我们可以从题目获取的信息是每行每列至多两个棋子
肥肠容易想到暴力算法:按行处理,枚举放在哪一列
但是只能水30
思考了一会儿,发现“方案数”这个关键词,容易想到组合数学
再想了一会儿,就很容易想到只要满足每行不超两颗棋子,列可以只用枚举有1颗棋子,2颗棋子的数量,方案数用组合数求
于是得数组fi,j,k表放了前i行,有1颗棋子的列有j个,两颗棋子的列有k个
方程分类讨论:
不放
放一颗棋子:
1、这颗棋子放在空列上(要求:m-j-k>=1)
2、这颗棋子放在1列上(要求:j>=1)
放两颗棋子:
1、这两颗棋子都放在空列上(要求:m-j-k>=2)
2、这两颗棋子都放在1列上(要求j>=2)
3、这两颗棋子,一颗在空列,一颗在1列(要求:m-j-k>=1,j>=1)

#include <cstdio>
#define rep(i,a,b) for (i=a;i<=b;i++)
using namespace std;
long long f[105][105][105];
int n,m;
int i,j,k;
long long ans,p=9999973;
long long C_2(int n){
    return n*(n-1)/2;
}
int main()
{
    scanf("%d%d",&n,&m);
    f[0][0][0]=1;
    rep(i,0,n)
    rep(j,0,m)
    rep(k,0,m-j){
        f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%p;
        if (m-j-k>=1) f[i+1][j+1][k]=(f[i+1][j+1][k]+f[i][j][k]*(m-j-k))%p;
        if (m-j-k>=1&&j>=1) f[i+1][j][k+1]=(f[i+1][j][k+1]+f[i][j][k]*(m-j-k)*j)%p;
        if (j>=1) f[i+1][j-1][k+1]=(f[i+1][j-1][k+1]+f[i][j][k]*j)%p;
        if (m-j-k>=2) f[i+1][j+2][k]=(f[i+1][j+2][k]+f[i][j][k]*C_2(m-j-k))%p;
        if (j>=2) f[i+1][j-2][k+2]=(f[i+1][j-2][k+2]+f[i][j][k]*C_2(j))%p;
    }
    rep(i,0,m)
    rep(j,0,m)
    ans=(ans+f[n][i][j])%p;
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/ssl_qyh0ice/article/details/80756018
今日推荐