[bzoj1801][Ahoi2009]chess 中国象棋

来自FallDream的博客,未经允许,请勿转载, 谢谢。


在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.

n,m<=100

 

直接状压dp不可能了

但是发现所有的列只有放了几个会影响答案,它是哪列无所谓,转移都相同。

所以可以用f[i][j][k]表示前i行有j个列没放,k个列放了1个的状态数,转移的时候计算一下这么转移的方案数即可。

#include<iostream>
#include<cstdio>
#define MN 100
#define mod 9999973
#define ll long long
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int f[MN+5][MN+5][MN+5],n,m;
inline void R(int&x,ll y){x=(1LL*x+y)%mod;}
int main()
{
    n=read();m=read();
    f[0][m][0]=1;
    for(int i=1;i<=n;++i)
        for(int j=0;j<=m;++j) 
            for(int k=0;k<=m;++k)
                if(f[i-1][j][k])
                {
                    if(j>0) R(f[i][j-1][k+1],1LL*j*f[i-1][j][k]);
                    if(j>1) R(f[i][j-2][k+2],1LL*j*(j-1)/2*f[i-1][j][k]);
                    if(k>0) R(f[i][j][k-1],1LL*k*f[i-1][j][k]);
                    if(k>1) R(f[i][j][k-2],1LL*k*(k-1)/2*f[i-1][j][k]);
                    if(j&&k)R(f[i][j-1][k],1LL*j*k*f[i-1][j][k]);
                    R(f[i][j][k],f[i-1][j][k]);
                }
    int ans=0;
    for(int j=0;j<=m;++j)
        for(int k=0;k<=m;++k)
            R(ans,f[n][j][k]);
    printf("%d\n",ans);        
    return 0;
}
posted @ 2017-05-17 17:27  FallDream  阅读(244)  评论(0编辑  收藏  举报