[AHOI2009]中国象棋
2017-12-04 15:56
302 查看
题目描述
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。
输出格式:
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
数据范围
100%的数据中N和M均不超过100
50%的数据中N和M至少有一个数不超过8
30%的数据中N和M均不超过6
显然,同一行里我们最多放2个棋子。
限制我们放棋子的条件是纵列。
我的初步想法是,状压把当前行表示出来,记录前面的列放了棋子数,合法转移。
先不说这样一定会T。。代码实现就很难。要用到三进制状压DP。
这样就很麻烦。。而且n,m是100。根本就没法做。。
然而对于任何一个状态,我们并不关心他的具体内容,只关心方案数。我们可不可以处理出转移的方案数,而不表示具体状态?
综上:我们需要一种状态,既可以体现出纵列的约束,又不表示具体状态使得转移方便。
所以f【i】【j】【k】表示前i行,有j列放了一个棋子,有k列放了2个棋子。
显然,初始状态f【0】【0】【0】。
对于每一行,我们的操作有:
1.不放 直接转移
2.放一个在无棋子的列。(有n列状态数则*n)
3.放在一个有棋子的列。(有n列状态数*n)
4.放2个,都在无棋子的列(C n 2)
5.放2个,都在有1个棋子的列(C n 2)
6.放2个,一个在无棋子的列,一个在有棋子的列(乘法原理 n*m)。
然后转移最后求一下 Σf【n】【j】【k】就好了。
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。
输出格式:
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
数据范围
100%的数据中N和M均不超过100
50%的数据中N和M至少有一个数不超过8
30%的数据中N和M均不超过6
显然,同一行里我们最多放2个棋子。
限制我们放棋子的条件是纵列。
我的初步想法是,状压把当前行表示出来,记录前面的列放了棋子数,合法转移。
先不说这样一定会T。。代码实现就很难。要用到三进制状压DP。
void init()//预处理三进制状态每一位的值 { state[0]=1; for(int i=1;i<=10;i++) state[i]=state[i-1]*3; for(int i=0;i<=state[10];i++){ int tmp=i; for(int j=0;j<=10;j++){ vis[i][j]=tmp%3; tmp/=3; } }
这样就很麻烦。。而且n,m是100。根本就没法做。。
然而对于任何一个状态,我们并不关心他的具体内容,只关心方案数。我们可不可以处理出转移的方案数,而不表示具体状态?
综上:我们需要一种状态,既可以体现出纵列的约束,又不表示具体状态使得转移方便。
所以f【i】【j】【k】表示前i行,有j列放了一个棋子,有k列放了2个棋子。
显然,初始状态f【0】【0】【0】。
对于每一行,我们的操作有:
1.不放 直接转移
2.放一个在无棋子的列。(有n列状态数则*n)
3.放在一个有棋子的列。(有n列状态数*n)
4.放2个,都在无棋子的列(C n 2)
5.放2个,都在有1个棋子的列(C n 2)
6.放2个,一个在无棋子的列,一个在有棋子的列(乘法原理 n*m)。
然后转移最后求一下 Σf【n】【j】【k】就好了。
#include<bits/stdc++.h> using namespace std; #define ll long long const ll mod=9999973; const ll MAXN=105; ll n,m; inline ll CX2(int X){ return X*(X-1)/2; } ll dp[MAXN][MAXN][MAXN];//f[i][j][k]表示 前i行,有j列放了1个,有k列放了2个。 //j+k+空=m int main(){ memset(dp,0,sizeof(dp)); scanf("%lld%lld",&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++){ if(dp[i][j][k]){//可以略省时间 (dp[i+1][j][k]+=dp[i][j][k])%=mod;//不放 if(j+k+1<=m*2&&m-j-k>0) (dp[i+1][j+1][k]+=(dp[i][j][k]*(m-j-k)))%=mod;//放一个放在空处 if(j+k+1<=m*2&&j>=1) (dp[i+1][j-1][k+1]+=(dp[i][j][k]*j))%=mod;//放一个在1处 if(j+k+2<=m*2&&m-j-k>1) (dp[i+1][j+2][k]+=(dp[i][j][k]*CX2(m-j-k)))%=mod;//2个都在0处放且不是一个地方 if(j+k+2<=m*2&&j>=2) (dp[i+1][j-2][k+2]+=(dp[i][j][k]*CX2(j)))%=mod;//都在1处放 if(j+k+2<=m*2&&m-j-k>0&&j>=1) (dp[i+1][j][k+1]+=(dp[i][j][k]*((m-j-k)*j)))%=mod;//一个放0,一个放1. } } ll ans=0; for(int j=0;j<=m;j++) for(int k=0;k+j<=m;k++){ (ans+=dp [j][k])%=mod; } printf("%lld\n",ans); return 0; }
相关文章推荐
- P2051 [AHOI2009]中国象棋
- BZOJ1801:[AHOI2009]中国象棋——题解
- [AHOI2009]中国象棋
- 1801: [Ahoi2009]chess 中国象棋
- bzoj1801[AHOI2009]CHESS中国象棋
- bzoj 1801: [Ahoi2009] chess 中国象棋
- bzoj1801 [Ahoi2009]chess 中国象棋 DP
- BZOJ1801 [Ahoi2009]chess 中国象棋 【dp】
- bzoj1801: [Ahoi2009]chess 中国象棋(Dp)
- DP【p2051(bzoj 1801)】 [AHOI2009]中国象棋.
- BZOJ1801: [Ahoi2009]chess 中国象棋
- 1801: [Ahoi2009]chess 中国象棋
- [AHOI2009]中国象棋
- 【bzoj1801】[Ahoi2009]chess 中国象棋
- BZOJ_1801_[Ahoi2009]chess 中国象棋_DP
- Luogu P2051[AHOI2009]中国象棋【dp】By cellur925
- bzoj1801【AHOI2009】chess 中国象棋
- bzoj1801 [Ahoi2009]chess 中国象棋
- [BZOJ 1801] [Ahoi2009]chess 中国象棋 【DP】
- AHOI2009中国象棋