[AHOI2009]中国象棋

传送门

分析

  • 显然对于每一行每一列我们都可以知道至多放2个炮。
  • 那么每一列只有3种情况: 1.还没有放置过 2.已经放置了1个 3.已经放置过2个了。
  • 那么每一行是不是就是一个子问题,我们用一维来表示已经处理到哪里了,这样这个DP就符合最优子结构了。
  • 那么我们就可以参考 移动服务 的优化后的状态方程设计方程啦。
  • d p [ i ] [ l ] [ r ] 表 示 前 i 行 , l 列 有 一 个 炮 , r 列 有 两 个 炮 dp[i][l][r]表示前i行,l列有一个炮,r列有两个炮 dp[i][l][r]ilr
  • 那么没有放置过的也就可以得到了
  • 对于每一行我们都是有三种情况的:1.不放炮 2.放1个炮 3.放两个炮
  • 对于不放置的情况,我们直接从上面一层拉过来即可: d p [ i ] [ l ] [ r ] + = d p [ i − 1 ] [ l ] [ r ] dp[i][l][r]+=dp[i-1][l][r] dp[i][l][r]+=dp[i1][l][r]
  • 如果放置一个的时候选择的列正好是没有放置过炮的: d p [ i ] [ l ] [ r ] + = d p [ i − 1 ] [ l − 1 ] [ r ] dp[i][l][r]+=dp[i-1][l-1][r] dp[i][l][r]+=dp[i1][l1][r]
  • 如果放置一个的时候选择的列正好是放置了一个炮的: d p [ i ] [ l ] [ r ] + = d p [ i − 1 ] [ l + 1 ] [ r − 1 ] dp[i][l][r]+=dp[i-1][l+1][r-1] dp[i][l][r]+=dp[i1][l+1][r1]
  • 如果放置两个的时候选择的列正好是没有放置过炮的: d p [ i ] [ l ] [ r ] + = d p [ i − 1 ] [ l − 2 ] [ r ] ∗ C ( m − l + 2 − r , 2 ) dp[i][l][r]+=dp[i-1][l-2][r]*C(m-l+2-r,2) dp[i][l][r]+=dp[i1][l2][r]Cml+2r2
  • 如果放置两个的时候选择的列正好是放置了一个炮的: d p [ i ] [ l ] [ r ] + = d p [ i − 1 ] [ l + 2 ] [ r − 2 ] ∗ C ( l + 2 ) dp[i][l][r]+=dp[i-1][l+2][r-2]*C(l+2) dp[i][l][r]+=dp[i1][l+2][r2]C(l+2
  • 如果放置两个的时候选择的列是放置一个炮和没有放置的: d p [ i ] [ l ] [ r ] + = d p [ i − 1 ] [ l ] [ r − 1 ] ∗ ( m − l − r + 1 ) ∗ l dp[i][l][r]+=dp[i-1][l][r-1]*(m-l-r+1)*l dp[i][l][r]+=dp[i1][l][r1](mlr+1)l

代码

#include<bits/stdc++.h>
using namespace std;
const int M=9999973;
long long dp[110][110][110];
int n,m;
inline long long C(int n)
{
	return n*(n-1)/2;
}
inline int read()
{
	int Num=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9') {Num=(Num<<1)+(Num<<3)+ch-'0'; ch=getchar();}
	return Num*f;
}
int main()
{
	n=read(),m=read();
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++)
	for(int l=0;l<=m;l++)
	for(int r=0;r<=m-l;r++)
	{
		dp[i][l][r]=dp[i-1][l][r]; 
		if(l>=1) dp[i][l][r]+=dp[i-1][l-1][r]*(m-(l-1)-r)%M;
		if(r>=1) dp[i][l][r]+=dp[i-1][l+1][r-1]*(l+1)%M;
		dp[i][l][r]%=M;
		if(l>=2) dp[i][l][r]+=dp[i-1][l-2][r]*C((m-(l-2)-r))%M;
		if(r>=1) dp[i][l][r]+=dp[i-1][l][r-1]*(m-l-(r-1))*l%M;
		if(r>=2) dp[i][l][r]+=dp[i-1][l+2][r-2]*C(l+2)%M;
		dp[i][l][r]%=M;
	}
	long long minn=0;
	for(int l=0;l<=m;l++)
		for(int r=0;r<=m;r++)
			minn=(minn+dp[n][l][r])%M;
	printf("%lld\n",minn%M);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值