• HDU 6415 Rikka with Nash Equilibrium (计数DP)


    题意:给两个整数n,m,让你使用 1 ~ n*m的所有数,构造一个矩阵n*m的矩阵,此矩阵满足:只有一个元素在它的此行和此列中都是最大的,求有多种方式。

    析:根据题意,可以知道那个元素一定是 n * m,因为这个数是最大的,不会有其他可能了,我们考虑从大小到的顺序放,先放最大的,再放次大的,那么想想次大的位置应该是在哪呢,必然是在最大数的所有的行或者是所有的列,因为如果不这样做,那么它一定也是它所在行和列中最大的了,就不满足条件了,同样再放第三大的,也是要放到第一大或者是第二大的所有行或者是列中,同理其他也是这样。所以就有了状态方程,dp[i][j][k] 表示,i 行中已经放过数,j 列中已经放过数了,最后放的数是 k,因为正着放和反着放结果是一样的,所以我们可以正着放,也就是按照 1 ~ n*m放,转移方程如下:

    1.考虑先增加新的一行,那么就是在已经存在的所有列中选择一列,然后再在该列中选择一个位置(此位置不能是行与列的交叉点)也就 dp[i][j][k] = dp[i-1][j][k-1] * j * (n-i+1)

    2.考虑都加新的一列,那么就是在已经存在的所有行中选择一行,然后再在该列中选择一个位置(此位置不能是行与列的交叉点),也就是 dp[i][j-1][k-1] * i * (m-j+1)

    3.考虑放到行与列的交叉点上,dp[i][j][k] = dp[i][j][k-1] * (i*j-k+1)。

    再考虑可以使用滚动数组进行优化,当然也可以不用优化。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn = 80 + 7;
    int n, m;
    int dp[2][maxn][maxn];
    
    int main(){
      int T;  scanf("%d", &T);
      while(T--){
        int K;
        scanf("%d %d %d", &n, &m, &K);
        memset(dp[0], 0, sizeof dp[0]);
        dp[0][1][1] = n * m % K;
        int cur = 1;
        for(int k = 2; k <= n * m; ++k, cur ^= 1){
          memset(dp[cur], 0, sizeof dp[cur]);
          for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j){
              if(i * j < k)  continue;
              dp[cur][i][j] = ((LL)dp[cur^1][i][j] * (i*j-k+1) % K + (LL)dp[cur^1][i-1][j] * j * (n-i+1) % K + (LL)dp[cur^1][i][j-1] * i * (m-j+1)% K) % K;
            }
        }
        printf("%I64d
    ", dp[cur^1][n][m]);
      }
      return 0;
    }
    

      

  • 相关阅读:
    博弈论(SG函数):HNOI 2007 分裂游戏
    博弈论(二分图匹配):NOI 2011 兔兔与蛋蛋游戏
    博弈论(男人八题):POJ 1740 A New Stone Game
    动态规划(树形DP):HDU 5834 Magic boy Bi Luo with his excited tree
    杂项(最小表示法):HZOI 2015 Glass Beads
    如何避免死锁
    死锁的四个必要条件
    线程安全和可重入函数之间的区别和联系
    信号量 sem_undo设置
    linux管道的容量和内部组织方式
  • 原文地址:https://www.cnblogs.com/dwtfukgv/p/11502331.html
Copyright © 2020-2023  润新知