• 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
    ", ans % mod);
    
        //while (1);
        return 0;
    }
  • 相关阅读:
    ajax 新闻栏目
    ajax 瀑布流实现
    html5 canvas图片渐变
    html5 canvas图片翻转
    html5 canvas图片反色
    ajax 第一个程序
    ajax 新闻栏目
    HDU 1756 Cupid's Arrow( 判断点在多边形的内外 )
    POJ 2356 Find a multiple( 鸽巢定理简单题 )
    HDU 5762 Teacher Bo ( 暴力 )
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7351360.html
Copyright © 2020-2023  润新知