bzoj1801 [Ahoi2009]chess 中国象棋 DP

168 篇文章 0 订阅

题意:用中国象棋中的规矩,在棋盘上摆炮要求不能被攻击,求方案数。
明显一列一行不能超过2个。
那么我们就可以以此转移,考虑一行行的来放,那么dp只跟行与行之间的关系有关。
设f[i][j][k]表示前i-1行做完了,现在做第i行,有j列只有一个棋子,k列只有两个棋子。
行的限制我们可以手动枚举,列的限制就放在dp里面去做。
那么有6种转移,至于如何实现,请看代码,这里不进行详写。
其实主要分为三大类。
一.放0个
直接继承
二.放1个
分别由原来没放棋子的列或者原来放了一个棋子的列分别转移而来。
三.放2个。
考虑这两个棋子放在哪里。
1.原来只有一个的列,原来没有棋子的列。
2.都放在原来没有棋子的列。
3.都放在原来只有一个棋子的列。
/喷血,如果是由i-1转移到i就没有问题,但是由i转移到i+1就会GG,可能有人能成功吧,因为转移以后j,k的范围变成了最多m+2,这导致有点难以统计,直接加起来是不对的,不知道该如何统计答案了。。有人知道的告诉我一下,WA了个爽。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m;
const int N=115;
const int mo=9999973;
typedef long long ll;
//int C[N][N]; 
ll f[N][N][N];
inline void add(ll &x,ll y)
{
    x=(x+y)%mo;
}
int main()
{
    //pre();
    scanf("%d%d",&n,&m);
    memset(f,0,sizeof(f));
    f[0][0][0]=1;
    fo(i,1,n)
    {
        fo(j,0,m)
        {
            fo(k,0,m-j)
            {
                f[i][j][k]=f[i-1][j][k];
                if (j>=1) (f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1))%=mo;  
                if (k>=1) (f[i][j][k]+=f[i-1][j+1][k-1]*(j+1))%=mo;  
                if (j>=2) (f[i][j][k]+=f[i-1][j-2][k]*(m-j-k+2)*(m-j-k+1)/2)%=mo;  
                if (k>=2) (f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2)%=mo;  
                if (j>=1&&k>=1) (f[i][j][k]+=f[i-1][j][k-1]*(m-j-k+1)*j)%=mo; 
            }   
        }
    }
    ll ans=0;
    fo(j,0,m)
    fo(k,0,m-j)ans=(ans+1ll*f[n][j][k])%mo;
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值