HDU-Chess 递推

这题一开始想用状态压缩DP解,后来发现状态开不下...还是没有很好的理解啊。

这里将棋盘看做是两个正方形,由于只能够走对角线,所以两个正方形可以看做是无关的,因此我们只要求出每个正方形走相应步数的方案。对于k不而言,在两个正方形里面就有(1, k-1), (2, k-2)...例如这样的分法,必须保证所有的分法都是合法的。现在问题就在于如何去求一个N*N的矩阵能放置一些棋子,这些棋子要求上下左右不能够在同行同列的方案数。其实就是一个简单的递推而已,我们定义dp[i][j]表示到第i行放置j个方案数,那么dp[i][j] = dp[i-1][j] + dp[i-1][j-1]*(n-j+1),其实也就是一个放与不放的选择。最后组合就能够得到答案了。

代码如下:

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 55
#define MOD 100007
using namespace std;

int N, M, dp[2][MAXN][MAXN];

// dp[.][i][j] 表示到第i行放置了j个方案数 dp[,][i][j] = C(j, i) * A(j, N)
// C(j, i) * A(j , N) = C(j, i-1) * A(j, N) + C(j-1, i-1) * A(j, N) = C(j, i-1) * A(j, N) + (N-j+1) * C(j-1, i-1) * A(j-1, N)
// 上式也即 dp[.][i][j] = dp[.][i-1][j] + dp[.][i-1][j-1] * (N - j + 1) 

void DP(int f, int n) {
    dp[f][0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        dp[f][i][0] = 1;
        for (int j = 1; j <= i; ++j) {
            dp[f][i][j] = dp[f][i-1][j]; // 第i行放或者是不放
            dp[f][i][j] += dp[f][i-1][j-1] * (n - j + 1);
            dp[f][i][j] %= MOD;
        }
    }
}

int main() {
    int a, b, ret;
    while (scanf("%d %d", &N, &M) == 2) {
        if (M > N) {
            puts("0");
            continue;
        }
        ret = 0;
        a = (N + 1) >> 1;
        b = (N - 1) >> 1;
        DP(0, a), DP(1, b);
        for (int i = 0; i <= M; ++i) {
            if (i > a || (M-i) > b) continue;
            ret += (long long)dp[0][a][i] * dp[1][b][M-i] % MOD;
            ret %= MOD;
        }
        printf("%d\n", ret);
    }
    return 0;    
}
posted @ 2012-08-17 01:39  沐阳  阅读(300)  评论(0编辑  收藏  举报