首页 > 代码库 > bzoj1801 [Ahoi2009]chess 中国象棋

bzoj1801 [Ahoi2009]chess 中国象棋

1801: [Ahoi2009]chess 中国象棋

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1868  Solved: 1075
[Submit][Status][Discuss]

Description

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

Input

一行包含两个整数N,M,中间用空格分开.

Output

输出所有的方案数,由于值比较大,输出其mod 9999973

Sample Input

1 3

Sample Output

7

HINT

除了在3个格子中都放满炮的的情况外,其它的都可以.

100%的数据中N,M不超过100
50%的数据中,N,M至少有一个数不超过8
30%的数据中,N,M均不超过6

分析:观察50%的数据,发现有一个数非常小,符合状压dp的条件,于是我们考虑每一行怎么放,用状压dp能过50分.

     那么怎么样才能通过全部分呢?状压dp比较耗时的原因是枚举每一行的状态用时太多,我们每次都要考虑每一列放或不放,要考虑2^m次,但是这道题只要求计数啊,所以我们完全不必记录每一行到底怎么放,我们只需要考虑有几行怎么放就可以了,也就是说,我们不必考虑每一行具体的怎么放,我们只需要考虑每一行中有多少列不放,放一个,放两个即可。

     那么设f[i][j][k]为前i行中有j列放1个,有k列放2个的方案数,可以发现每一行最多只能放两个,那么这些炮可以不放,放在只放了一个的列里,放在没有放的列里,并考虑一下放几个,状态转移方程就出来了,不过这个方程实在是太复杂,可以看代码理解:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int mod = 9999973;

int n, m,ans;
long long f[110][110][110];

int C(int x)
{
    return x * (x - 1) / 2;
}

int main()
{
    scanf("%d%d", &n, &m);
    f[0][0][0] = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= m; j++)
        {
            for (int k = 0; k <= m; k++)
            {
                f[i][j][k] = (f[i][j][k] + f[i - 1][j][k]) % mod;   //不放
                if (j >= 1)
                    f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - j + 1 - k) % mod) % mod; //在没有的列上放一个
                if (k >= 1)
                    f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1) % mod) % mod; //在只有一个的列上放一个
                if (j >= 2)
                    f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * C(m - j + 2 - k) % mod) % mod;   //在没有的列上放二个
                if (k >= 2)
                    f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * C(j + 2) % mod) % mod;  //在只有一个的列上放二个
                if (k >= 1)
                    f[i][j][k] = (f[i][j][k] + f[i-1][j][k - 1] * j * (m - j - k + 1) % mod ) % mod; //在有一个和没有的列上放二个
            }
        }
    }
    for (int i = 0; i <= m; i++)
        for (int j = 0; j <= m; j++)
            ans = (ans + f[n][i][j]) % mod;
    printf("%d\n", ans % mod);

    //while (1);
    return 0;
}

 

bzoj1801 [Ahoi2009]chess 中国象棋