码迷,mamicode.com
首页 > 其他好文 > 详细

P2051 [AHOI2009]中国象棋

时间:2020-09-11 14:15:12      阅读:27      评论:0      收藏:0      [点我收藏+]

标签:象棋   space   define   大致   i++   efi   cpp   names   观察   

大致题意

给一个\(n×m\)的棋盘,在上面放若干个炮,求有多少种放置方法可以使没有一个炮可以攻击到另一个炮

分析

状压\(dp\)

观察发现,每行和每列至多只能放\(2\)个棋子

考虑到每列中已经摆放的棋子数量会影响到之后能摆放的棋子数,不妨设:

\(f[i][j][k]\)为前\(i\)行中,一共有\(j\)列放了一个棋子的列数,\(k\)列放了两个棋子的列数

考虑这一行中放的棋子的个数

1.不放:

\(f[i+1][j][k] += f[i][j][k]\)

2.放一个:

  • 放在有一个棋子的列上:

此时有一个棋子的列数会减一,有两个的列数会加一

\(f[i+1][j-1][k+1] +=f[i][j][k]×j\)

  • 放在一个都没有的列上

此时有一个棋子的列数会加一,有两个的列数不变

\(f[i+1][j+1][k] +=f[i][j][k]×(m-j-k)\)

3.放两个

  • 全部放在一个都没有的列上

此时有一个棋子的列数会加二,有两个的列数不变

\(f[i+1][j+1][k] +=f[i][j][k]×C(m-j-k)\)

  • \(2:\)全部放在只有一个的列上

此时有一个棋子的列数会减二,有两个的列数会加二

\(f[i+1][j-2][k+2] +=f[i][j][k]×C(j)\)

  • 一个放在只有一个棋子的列上,一个放在没棋子的列上

此时有一个棋子的列数不变,有两个棋子的列数会加一

\(f[i+1][j][k+1] +=f[i][j][k]×(m-j-k)×j\)

\(code:\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 110;
#define int long long
#define mo 9999973
#define C(x) (((x)*(x-1)/2)%mo)
int f[MAXN][MAXN][MAXN];
int n,m;
signed main(){
	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+j<=m;k++){
				if(f[i][j][k]){
					f[i+1][j][k] = (f[i][j][k]+f[i+1][j][k])%mo;
					if(j>=1) f[i+1][j-1][k+1] = (f[i+1][j-1][k+1]+f[i][j][k]*j)%mo;
					if(j>=1&&(m-j-k)>0) f[i+1][j][k+1] =(f[i+1][j][k+1]+f[i][j][k]*(m-j-k)*j)%mo;
					if((m-j-k)>0) f[i+1][j+1][k] = (f[i+1][j+1][k]+f[i][j][k]*(m-j-k))%mo;
					if(j>=2) f[i+1][j-2][k+2] = (f[i+1][j-2][k+2]+f[i][j][k]*C(j))%mo;
					if((m-j-k)>1) f[i+1][j+2][k] = (f[i+1][j+2][k]+f[i][j][k]*C(m-j-k))%mo;
				}
			}
		}
	}
	int ans = 0;
	for(int i=0;i<=m;i++){
		for(int j=0;j+i<=m;j++){
			ans = (ans+f[n][i][j])%mo;
		}
	}
	cout<<ans%mo;
}

坑点&&感想

  • 注意边界

  • 转移时若不关心具体位置,考虑直接记录该状况的数量

P2051 [AHOI2009]中国象棋

标签:象棋   space   define   大致   i++   efi   cpp   names   观察   

原文地址:https://www.cnblogs.com/xcxc82/p/13586341.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!