luogu 2051 [AHOI2009]中国象棋

题目描述

这次小可可想解决的难题和中国象棋有关,在一个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

分析

鬼知道其实我一开始想用五维

好吧,不可行

题解链接:题解 P2051 【[AHOI2009]中国象棋】 

part 1 审题

首先,整理题意:

每行每列只能放至多两粒棋子

part 2 状态

好的,一格一格来是不可行了(看我的五维就知道了)

既然状态明显以行列为单位,那状态就以行列设计

暂定,需要存的状态:行,列,棋子数

其实现在是真不知道要考虑什么具体状态,先分析

part 3 转移

说实话,格不行,那就一列一列枚举

 再考虑棋子,在当前行,我们可以选择:

1. 不放棋子

2. 放一粒棋子

3. 放两粒棋子

废话

emmmm,好吧。

我们放棋子的时候,会造成什么影响?

1. 有些没棋子的列,有棋子了

2. 有些只有一个棋子的列,有两个棋子了

3. 有些有两个棋子的列……好吧不能在这一列放棋子了

嗯?有什么用?用处可大了


你想想,我们需要求的是什么?

方案数。

如果我们能求出这一行不放、放一粒棋子、放两粒棋子的方案数,这问题就圆满了

怎么求?

再看看上面的分析

当我们不放棋子的时候,方案数就等于上一行求出的总方案数

当我们放一粒棋子、放两粒棋子的时候,列的状态会改变

我们该怎么表示列的状态?然后存下方案数?

放棋子,列上的棋子数在增加,我们当然不可能存下每一列的具体状态,毕竟我们不求具体的摆放方案

列的状态与棋子数有关,却与棋子具体在哪一行无关

棋子具体在哪一行,或行上的棋子具体在哪一列,只对于方案数有贡献(然而这个可以用加法、乘法原理解决),对于接下来棋子的摆放无关联

好的,状态来了

如上,我们只存下列上的具体棋子数的列数就够了

状态:

dp[第i行][只有一个棋子的列有j列][有两个棋子的列有k列] = 方案数

你问我为啥没有空列的个数?

不就是m-j-k嘛

好吧,怎么求方案?

看看我们曾经的分析:

再考虑棋子,在当前行,我们可以选择:

1. 不放棋子

2. 放一粒棋子

3. 放两粒棋子

我们放棋子的时候,会造成什么影响?

1. 有些没棋子的列,有棋子了

2. 有些只有一个棋子的列,有两个棋子了

3. 有些有两个棋子的列……好吧不能在这一列放棋子了

好的

状态转移出来了

分三种情况讨论,不放,放一粒,放两粒,分别计算

不放的时候:

放一粒棋子:

放两粒棋子:

这里解释一下放两粒棋子的转移:

放空列:dp[i][j][k] += dp[i-1][j-2][k] * C2m-(j-2)-k

放在有一个棋子的列:dp[i][j][k] += dp[i-1][j+2][k-2] * C2j+2

一个空列,一个有棋:dp[i][j][k] += dp[i-1][j-1+1][k-1] * C1m-j-(k-1) * C1j

求组合数:emm,通过观察,1的就是本身,2的可以这么求:

好了,over

代码

 1 /**************************
 2 User:Mandy.H.Y
 3 Language:c++
 4 Problem:luogu 2051
 5 Algorithm:
 6 **************************/
 7 
 8 #include<bits/stdc++.h>
 9 #define Max(x,y) ((x) > (y) ? (x) : (y))
10 #define Min(x,y) ((x) < (y) ? (x) : (y))
11 
12 using namespace std;
13 
14 const int maxn = 105;
15 const int mod = 9999973;
16 
17 int n,m;
18 long long dp[maxn][maxn][maxn];
19 
20 template<class T>inline void read(T &x){
21     x = 0;bool flag = 0;char ch = getchar(); 
22     while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
23     while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
24     if(flag) x = -x;
25 }
26 
27 template<class T>void putch(const T x){
28     if(x > 9) putch(x / 10);
29     putchar(x % 10 | 48);
30 }
31 
32 template<class T>void put(const T x){
33     if(x < 0) putchar('-'),putch(-x);
34     else putch(x);
35 }
36 
37 void file(){
38     freopen("2051.in","r",stdin);
39     freopen("2051.out","w",stdout);
40 }
41 
42 void readdata(){
43     read(n);read(m);
44 }
45 
46 int C(int x){
47     return x*(x-1)/2;
48 }
49 
50 void work(){
51     
52     dp[0][0][0] = 1;
53     
54     for(int i = 1;i <= n; ++ i){
55         for(int j = 0;j <= m; ++ j){
56             for(int k = 0;k <= m - j; ++ k){
57                 
58                 dp[i][j][k] = dp[i - 1][j][k];//第i行不放 
59                 
60                 if(j > 0) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j-1][k] * (m-j+1-k)) % mod;
61                 //第i行放一个,放在空列 
62                 
63                 if(k > 0) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j+1][k-1] * (j+1)) % mod;
64                 //第i行放一个,放在有一个棋子的列 
65                 
66                 if(j > 1) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j-2][k] * C(m-j+2-k)) % mod;
67                 //第i行放两个,放在空列 
68                 
69                 if(k > 1) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j+2][k-2] * C(j+2)) % mod;
70                 //第i行放两个,放在有一个棋子的列 
71                 
72                 if(k > 0) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j][k-1] * (m-j-k+1) % mod * j) % mod;  
73                 //注意是 * (m-j-k+1) * j 
74                 //第i行放两个,一个放在有一个棋子的列 ,一个放空列 
75             
76             }
77         }
78     }
79     long long ans = 0;
80     for(int i = 0;i <= m; ++ i)
81         for(int j = 0;j <= m - i; ++ j){//m - i
82             ans = (ans + dp[n][i][j]) % mod;
83         }
84         
85     put(ans);
86 }
87 
88 int main(){
89 //    file();
90     readdata();
91     work();
92     return 0;
93 }
View Code

 

非做顽石不可,哪管他敬仰暗唾
原文地址:https://www.cnblogs.com/Mandy-H-Y/p/11442727.html