洛谷P1005 [NOIP2007 提高组] 矩阵取数游戏

1.题目

题目描述

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n × m n\times m n×m 的矩阵,矩阵中的每个元素 a i , j a_{i,j} ai,j 均为非负整数。游戏规则如下:

  • 每次取数时须从每行各取走一个元素,共 n n n 个。经过 m m m 次后取完矩阵内所有元素;
  • 每次取走的各个元素只能是该元素所在行的行首或行尾;
  • 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 × 2 i \times 2^{i} ×2i ,其中 i i i 表示第 i i i 次取数(从 1 1 1 开始编号);
  • 游戏结束总得分为 m m m 次取数得分之和。

帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

输入格式

输入文件包括 n + 1 n+1 n+1 行:
第一行为两个用空格隔开的整数 n n n m m m
2 ∼ n + 1 2 \sim n+1 2n+1 行为 n × m n \times m n×m矩阵,其中每行有 m m m 个单个空格隔开的非负整数。

输出格式

输出文件仅包含 1 1 1 行,为一个整数,即输入矩阵取数后的最大得分。

输入输出样例

输入 #1

2 3
1 2 3
3 4 2

输出 #1

82
说明/提示

NOIP 2007 提高第三题
数据范围:
60 % 60\% 60% 的数据满足: 1 ≤ n , m ≤ 30 1 \le n,m \le 30 1n,m30 ,答案不超过 1 0 16 10^{16} 1016
100 % 100\% 100% 的数据满足: 1 ≤ n , m ≤ 80 1 \le n,m \le 80 1n,m80 0 ≤ a i , j ≤ 1000 0 \le a_{i,j} \le 1000 0ai,j1000

2.思路

主要思路:动态规划。由于各行取数的得分互不影响,则游戏总得分可以这样计算:分别对每行按照题目规则取数,共取 m m m 次,并计算该行的总得分,最后将所有行的总得分相加,即为游戏总得分。在对每行进行取数时,就需要采用动态规划的思路来计算最优解。

  • 步骤一:定义动态规划数组 d p dp dp
    由于取数时只能从该行的最左侧或最右侧取值,故定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示取数区间变为 [ i , j ] [i,j] [i,j] 时的最优解, i i i 是取数区间的最左侧, j j j 是取数区间的最右侧。
  • 步骤二:定义状态转移方程
    取数区间 [ i , j ] [i,j] [i,j] 是由区间 [ i − 1 , j ] [i-1,j] [i1,j] 取最左侧的数或 [ i , j + 1 ] [i, j+1] [i,j+1] 取最右侧的数之后变成的,则状态转移方程为 d p [ i ] [ j ] = max ⁡ { d p [ i − 1 , j ] + a k , i − 1 × 2 m − j + i − 1 , d p [ i , j + 1 ] + a k , j + 1 × 2 m − j + i − 1 } dp[i][j] = \max\{dp[i-1,j]+a_{k,i-1}\times 2^{m-j+i-1},dp[i,j+1]+a_{k,j+1}\times 2^{m-j+i-1}\} dp[i][j]=max{dp[i1,j]+ak,i1×2mj+i1,dp[i,j+1]+ak,j+1×2mj+i1}其中的 k k k 表示第 k k k 行。之所以 2 2 2 的幂次为 m − j + i − 1 m-j+i-1 mj+i1 ,是由于其和取数区间的长度有关:当取数区间变为 [ 1 , m ] [1,m] [1,m] 时,此时为第 m − m = 0 m-m=0 mm=0 次取数,即区间长度 m m m 对应第 0 0 0 次取数;每取一次数,区间长度减一,次数加一,故当取数区间变为 [ i , j ] [i,j] [i,j] 时,为第 m − ( j − i + 1 ) = m − j + i − 1 m-(j-i+1) = m-j+i-1 m(ji+1)=mj+i1 次取数。另外,最终取数区间将变成 [ i , i ] [i,i] [i,i] 的形式,对应 d p [ i ] [ i ] dp[i][i] dp[i][i] ,但并未加上 a k , i × 2 m a_{k,i} \times 2^m ak,i×2m ,因此,最终的最优解应从所有的 d p [ i ] [ i ] + a k , i × 2 m dp[i][i]+a_{k,i} \times 2^m dp[i][i]+ak,i×2m 中选择最大值,即 m a x = max ⁡ 1 ≤ i ≤ m { d p [ i ] [ i ] + a k , i × 2 m } max=\max_{1 \le i \le m}\{dp[i][i]+a_{k,i} \times 2^m\} max=1immax{dp[i][i]+ak,i×2m}这仅仅是第 k k k 行的最优解,游戏总得分应为所有行最优解之和。
  • 步骤三:设置初始值
    对于矩阵中的一行数,最开始的取数区间为 [ 1 , m ] [1,m] [1,m] d p [ 1 , m ] dp[1,m] dp[1,m] 的值应为 0 0 0 ,因为第 0 0 0 次取数自然取不出大于零的数,初始得分应为 0 0 0,则 d p [ 1 , m ] dp[1,m] dp[1,m] 可以看成取数区间 [ 0 , m ] [0, m] [0,m] 取走一个数 0 0 0 或取数区间 [ 1 , m + 1 ] [1,m+1] [1,m+1] 取走一个数 0 0 0 ,并令 d p [ 0 , m ] = d p [ 1 , m + 1 ] = 0 dp[0,m]=dp[1,m+1]=0 dp[0,m]=dp[1,m+1]=0 这样就与步骤二中的状态转移方程相同。对于区间最左侧为 1 1 1 的取数区间,自然不可能由区间 [ 0 , y ] [0,y] [0,y] 状态转移过来,因此令 d p [ 0 ] [ y ] = 0 dp[0][y]=0 dp[0][y]=0 ,并令所取得的数为 0 0 0 ,以满足步骤二中的状态转移方程。同理,对于区间最右侧为 m m m 的取数区间,自然不可能由区间 [ x , m ] [x,m] [x,m] 状态转移过来,因此令 d p [ x ] [ m ] = 0 dp[x][m]=0 dp[x][m]=0 ,并令所取得的数为 0 0 0 ,以满足步骤二中的状态转移方程。

3.代码

根据提示,最后的答案可能超出long类型的最大值,因此我们使用Java中的BigInteger类来作为动态规划数组等变量的数据类型。代码如下:

import java.math.BigInteger;
import java.util.Scanner;

/**
 * @author xiaoyaosheny
 * @discription 洛谷P1005 [NOIP2007 提高组] 矩阵取树游戏
 * @date 2021/7/10
 */
public class Main {

    /**
     * 程序入口
     *
     * @param args 输入参数
     */
    public static void main(String[] args) {
        // 获取输入
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[][] matrix = new int[n][m + 2];
        for (int i = 0; i < n; i++) {
            for (int j = 1; j < m + 1; j++) {
                matrix[i][j] = scanner.nextInt();
            }
        }
        // 解决问题
        BigInteger score = new BigInteger("0");
        for (int j = 0; j < n; j++) {
            // 动态规划数组
            BigInteger[][] dp = new BigInteger[m + 2][m + 2];
            for (int k = 0; k < m + 2; k++)
            {
                for (int l = 0; l < m + 2; l++)
                    dp[k][l] = new BigInteger("0");
            }
            // 动态规划
            for (int k = 1; k < m + 1; k++) {
                for (int l = m; l >= k; l--)
                {
                    BigInteger base2 = new BigInteger("2").pow(m - l + k - 1);
                    dp[k][l] = (dp[k - 1][l].add(new BigInteger(String.valueOf(matrix[j][k - 1])).multiply(base2))).max(dp[k][l + 1].add(new BigInteger(String.valueOf(matrix[j][l + 1])).multiply(base2)));
                }
            }
            BigInteger max = new BigInteger("0");
            for (int k = 1; k < m + 1; k++) {
                max = max.max(dp[k][k].add(new BigInteger(String.valueOf(matrix[j][k])).multiply(new BigInteger("2").pow(m))));
            }
            score = score.add(max);
        }
        // 输出结果
        System.out.println(score);
    }
}

4.参考

Jack_Homes_Huang的博客

2017游戏大厅大全,就在搜狗游戏大厅 2017火爆的网页游戏,就在搜狗游戏大厅点击下载游戏大厅, yxdt.sogou.com · 2018-1-27 · 搜狗广告 矩阵取数游戏 - Vijos 描述 帅帅经常更同学一个矩阵取数游戏:对于一个给定的n*m的矩阵矩阵中的每个元素aij据为非负整数。游戏规则如下: 1. 每次取数时须从每行各一个元素,共n个。 https://vijos.org/p/1378 p1378 - Vijos 矩阵取数游戏 (game.pas/c/cpp) 【问题描述】 帅帅经常跟同学一个矩阵取数游戏:对于一个给定的n*m的矩阵矩阵中的每个元素aij均为非负整数。游戏规则 ... https://vijos.org/discuss/5109fde44e4112501400074b 2009-9-15 错别字 - Vijos #论字打的快的后果 帅帅经常**更同学**一个矩阵取数游戏:对于一个给定的n×m的矩阵矩阵中的每个元素aij**据为**非负整数。 更同学->跟同学 据为非负 ... https://vijos.org/discuss/57c2840f17f3ca4a05f6a0ad 2016-8-28 我知道估计没人解答,我自己看了都长的程序 - Vijos 矩阵取数游戏 登录后创建讨论 查看题目 登录后递交 讨论 题解 信息 ID 1378 难度 6 分类 动态规划 | 高精度 点击显示 标签 NOIP 2007 提高 递交数 4072 已通过 956 23% ... https://vijos.org/discuss/5109fddf4e411250140003d0 2009-7-12 难得过了,简单的栽了。请指点! - Vijos / Vijos / 讨论 / 矩阵取数游戏 / 难得过了,简单的栽了。请指点! 矩阵取数游戏 Lemon tree LV 8 @ 2009-07-27 18:34:33 编译通过... ├ 测试数据 01 ... https://vijos.org/discuss/5109fde04e4112501400049d 2009-7-27 讨论 - Vijos
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值