zoukankan      html  css  js  c++  java
  • AHOI2009中国象棋

    首先以行为阶段,根据象棋的规则,在同一行中,至多只能有两个炮,同理:在同一列中,至多只能有两个炮
    思考一个可以覆盖整个状态空间的dp数组:
    dp[i]表示到了第i行
    接下来我们想:某列中的炮能否通过位运算求得
    我们能够发现,可能我们目前在第i行,但是在某个j行的p列有一个炮,我们要知道第i行的第p列能否放置炮。但是j可能与i相差甚远,我们不能直接通过位运算得到,逐行枚举又会耗费大量不必要的时间。
    那么我们就干脆将列的状态记录在数组里,我们想我们其实并不关心到第i行时哪一列有1个炮,哪一列有两个炮,我们只需要知道到第i行时,有多少列有1个炮,有多少列有两个炮,剩下的问题我们能够通过枚举状态解决
    这样就有了dp数组:
    dp[i][j][k]表示到第i行时,有j列有一个炮,k列有两个炮

    假设第i行只放一个炮,那么放置的方法数累加(DP方程)就是:
    1.这一个炮放在了原来没有炮的位置
    dp[i][j][k] += dp[i - 1][j - 1][k] * (m - j - k)
    2.这一个炮放在了原来有一个炮的位置
    dp[i][j][k] += dp[i - 1][j + 1][k - 1] * j

    假设第i行放置了两个炮
    1.这一行两个炮都放在了原来没有炮的位置
    dp[i][j][k] += dp[i - 1][j - 2][k] * (m - j - k) * (m - j - k - 1) / 2;
    2.这一行一个炮放在了原来有一个炮的位置,一个炮放在了原来没有炮的位置
    dp[i][j][k] += dp[i - 1][j][k - 1] * (m - j - k) * j
    3.这一行的两个炮都放在了原来有一个炮的位置
    dp[i][j][k] += dp[i - 1][j + 2][k - 2] * j * (j - 1) / 2;

    假设第i行没有放炮
    dp[i][j][k] += dp[i - 1][j][k]
    =-=??好像没了?接着就是处理一下每种情况能够使用的限制条件
    初态:dp[0][0][0] = 1;
    末态:Σdp[n][i][j]

    恩是的,这是我原本的思路,但我这么写后,不知道为什么就挂了。

    挂了!样例都过不了!

    于是我毅然决然的把有前驱推当前状态的写法改为了由当前状态推后继状态,然后就...A了.....

    方程差别不大,不做修改,直接看代码吧...

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 const int p = 9999973;
     5 const int maxn = 110;
     6 ll f[maxn][maxn][maxn];
     7 int n, m;
     8 
     9 inline  int read() {
    10     int x = 0, y = 1;
    11     char ch = getchar();
    12     while(!isdigit(ch)) {
    13         if(ch == '-') y = -1;
    14         ch = getchar();
    15     }
    16     while(isdigit(ch)) {
    17         x = (x << 1) + (x << 3) + ch - '0';
    18         ch = getchar();
    19     }
    20     return x * y;
    21 }
    22 
    23 inline int count(int k) {
    24 return k * (k - 1) / 2;}
    25 
    26 int main() {
    27     memset(f, 0, sizeof(f));
    28     n = read(), m = read();
    29     f[0][0][0] = 1;
    30     for(int i = 0; i < n; ++i)
    31         for(int j = 0; j <= m; ++j)
    32             for(int k = 0; k + j <= m; ++k)
    33                 if(f[i][j][k]) {
    34                     f[i + 1][j][k] = (f[i][j][k] + f[i + 1][j][k]) % p;
    35                     if(m - j - k >= 1) f[i + 1][j + 1][k] = (f[i + 1][j + 1][k] + f[i][j][k] * (m - j - k)) % p;
    36                     if(j >= 1) f[i + 1][j - 1][k + 1] = (f[i + 1][j - 1][k + 1] + f[i][j][k] * j) % p;
    37                     if(m - j - k >= 2) f[i + 1][j + 2][k] = (f[i + 1][j + 2][k] + f[i][j][k] * count(m - j - k)) % p;
    38                     if(m - j - k >= 1 && j >= 1) f[i + 1][j][k + 1] = (f[i + 1][j][k + 1] + f[i][j][k] * (m - j - k) * j) % p;
    39                     if(j >= 2) f[i + 1][j - 2][k + 2] = (f[i + 1][j - 2][k + 2] + f[i][j][k] * count(j)) % p;
    40                     f[i][j][k] %= p;
    41                 }
    42     ll ans = 0;
    43     for(int i = 0; i <= m; ++i)
    44         for(int j = 0; j + i <= m; ++j)
    45             ans = (ans + f[n][i][j]) % p;
    46     cout << ans << '
    ';
    47     return 0;
    48 } 
    View Code
  • 相关阅读:
    Spring中bean的生命周期
    HashMap与HashTable原理及数据结构
    HashMap 与HashTable的区别
    2 rocketmq mqadmin 的用法详解
    搭建RocketMQ踩的坑-内存不足
    解决Hash碰撞冲突方法总结
    大端BigEndian、小端LittleEndian与字符集编码
    Linux中最常用的JAVA_HOME配置
    WIP_DISCRETE_JOBS.STATUS_TYPE
    Inventory > INV.MTL_MATERIAL_TRANSACTIONS Show Error Msg: ORA-20100: File lxxx.tmp creation for FND_FILE failed.
  • 原文地址:https://www.cnblogs.com/ywjblog/p/9285664.html
Copyright © 2011-2022 走看看