BZOJ1801: [Ahoi2009]chess 中国象棋

1801: [Ahoi2009]chess 中国象棋

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1910  Solved: 1104
[Submit][Status][Discuss]

Description

在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.

Input

一行包含两个整数N,M,中间用空格分开.

Output

输出所有的方案数,由于值比较大,输出其mod 9999973

Sample Input

1 3

Sample Output

7

HINT

除了在3个格子中都放满炮的的情况外,其它的都可以.

100%的数据中N,M不超过100
50%的数据中,N,M至少有一个数不超过8
30%的数据中,N,M均不超过6

思路{

  在DP过程中发现问题的一些限制,特征,能够找到问题的突破口.

  比如此题:发现每行每列都不能放超过2个棋子,那么我们从上述限制设转移方程,

  f[i][j][k]为到第i行有j列放1个,k列放2个.

  仔细考虑清楚各种情况算组合数转移.

}

 

#include<bits/stdc++.h>
#define RG register
#define il inline 
#define N 200
#define mod 9999973
#define LL long long
using namespace std;
LL dp[N][N][N];int n,m;
LL c(int x){return (x*(x-1))/2;}
void update(LL & x){x%=mod;}
int main(){
  scanf("%d%d",&n,&m);
  dp[0][0][0]=1;
  for(int i=0;i<n;++i){
    for(int j=0;j<=m;++j)
      for(int k=0;k+j<=m;++k){
    dp[i+1][j][k]+=dp[i][j][k];update(dp[i+1][j][k]);
    if(m-j-k>0){
      dp[i+1][j+1][k]+=dp[i][j][k]*(m-j-k);
      update(dp[i+1][j+1][k]);//放一个在空位上
    }
    if(j){
      dp[i+1][j-1][k+1]+=dp[i][j][k]*j;
      update(dp[i+1][j-1][k+1]);//放一个在已经放过的上
    }
    if(m-j-k>0){
      dp[i+1][j+2][k]+=dp[i][j][k]*c(m-j-k);
      update(dp[i+1][j+2][k]);//放两个在空位上
    }
    if(m-j-k>0){
      dp[i+1][j][k+1]+=dp[i][j][k]*(m-j-k)*j;
      update(dp[i+1][j][k+1]);//2.1个空1个已放1
    }
    if(j>1){
      dp[i+1][j-2][k+2]+=dp[i][j][k]*c(j);
      update(dp[i+1][j-2][k+2]);//2.都在1
    }
      }
  }LL Ans(0);
  for(int i=0;i<=m;++i)
    for(int j=0;i+j<=m;++j)Ans+=dp[n][i][j],Ans%=mod;
  cout<<Ans;
  return 0;
}

 

posted @ 2017-09-04 23:32  QYP_2002  阅读(119)  评论(0编辑  收藏  举报