• 【题解】AHOI2009中国象棋


      还记得第一次看见这题的时候好像还是联赛前后的事了,那时感觉这题好强……其实现在看来蛮简单的,分类讨论一下即可。题意非常的简单:每一行,每一列都不能超过两个棋子。考虑我们的dp,如果一行一行转移的话行上不能超过两个棋子是很好满足的,就看列上如何满足了。所以状态自然而然的设置为 (f[i][j][k]),分别代表枚举到第 (i) 行,之前的列上有 (j) 列上有两个棋子,(k)列上有一个棋子时的方案数。

      然后分情况转移乘以组合数即可。虽然简单,但感觉还是有所启发:做组合数类型的DP,应该观察到对后续状态真正产生影响的要素,只需保留这几样作为状态即可,其余的任选都有组合数来体现(其实一般的DP也是这样吧(`・ω・´))代码里面有小小注释……个人喜欢打英文的(主要是懒得切输入法)。

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 200
    #define int long long
    #define mod 9999973
    int n, m, ans, f[maxn][maxn][maxn];
    int C[maxn][3];
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    void up(int &x, int y) { x = (x + y % mod) % mod; }
    
    void init()
    {
        for(int i = 0; i <= m; i ++) C[i][0] = 1;
        for(int i = 1; i <= m; i ++)
            for(int j = 1; j <= 2; j ++)
                if(j > i) C[i][j] = 0;
                else C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
    }
    
    signed main()
    {
        n = read(), m = read();
        f[0][0][0] = 1; init();
        for(int i = 0; i < n; i ++)
            for(int j = 0; j <= m; j ++)
                for(int k = 0; k <= m - j; k ++)
                {
                    if(!f[i][j][k]) continue;
                    up(f[i + 1][j][k], f[i][j][k]); // 0
                    int t = m - j - k, S = f[i][j][k];
                    // place a chess 
                    if(t) up(f[i + 1][j][k + 1], S * t); // placed on a row of 0; 
                    if(k) up(f[i + 1][j + 1][k - 1], S * k); // placed on a row of 1;
                    // place two chess
                    if(t >= 2) up(f[i + 1][j][k + 2], S * C[t][2]); //two on zero;
                    if(k && t) up(f[i + 1][j + 1][k], S * t * k); // one on zero, one on one;
                    if(k >= 2) up(f[i + 1][j + 2][k - 2], S * C[k][2]); // two on one;
                }
        for(int j = 0; j <= m; j ++)
            for(int k = 0; k <= m - j; k ++)
                up(ans, f[n][j][k]);
        printf("%lld
    ", ans);
        return 0;    
    } 
  • 相关阅读:
    基于STM32F103C8T6的超声波测距示例
    ST-LINK 到 SWD接线图
    MDK破解版下载
    CompletableFuture Quasar 等并发编程
    c++中参数传递和函数返回简析
    c++中冒号(:)和双冒号(::)的用法
    c++中,size_typt, size_t, ptrdiff_t 简介
    c++中,保证头文件只被编译一次,避免多重包含的方法
    时间序列分析之一次指数平滑法
    Openv2.1基本数据类型
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/9064545.html
Copyright © 2020-2023  润新知