zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    分区助手怎么调整磁盘分区的大小
    3dsmax2014的下载、安装与注册激活教程详解
    U深度U盘启动盘制作工具怎么用?U深度U盘启动盘制作工具使用教学
    CAD出现向程序发送命令时出现问题提示解决方法分享
    TeamViewer——可以实现在手机上随时远程控制你的电脑
    CPU-Z五大主要功能及使用方法初步了解
    vs中更改项目名称注意事项
    Oracle 存储过程例子返回记录集
    oracle 调用包体的函数并返回return值
    oracle 中更新update不成功的原因
  • 原文地址:https://www.cnblogs.com/dwtfukgv/p/11502331.html
Copyright © 2011-2022 走看看