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

题目描述

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

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

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

输入格式

输入文件包括 n+1n+1n+1 行:

第一行为两个用空格隔开的整数 nnn 和 mmm。

第 2∼n+12\sim n+12∼n+1 行为 n×mn \times mn×m 矩阵,其中每行有 mmm 个用单个空格隔开的非负整数。

输出格式

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

样例 #1

样例输入 #1

2 3
1 2 3
3 4 2

样例输出 #1

82

提示

【数据范围】

对于 60%60\%60% 的数据,满足 1≤n,m≤301\le n,m\le 301≤n,m≤30,答案不超过 101610^{16}1016。
对于 100%100\%100% 的数据,满足 1≤n,m≤801\le n,m\le 801≤n,m≤80,0≤ai,j≤10000\le a_{i,j}\le10000≤ai,j​≤1000。

还是想感叹,洛谷这个复制题目makrdown的快捷键是真的舒服,不想leetcode一样需要一点一点的复制

分析

看到这道题我的第一个想法是,这不就是很简单的贪心题吗?
但是,但是,虽然用贪心能过样例,但是只能得20分(两个点)

就拿测试数据2的一行数据为反例:876 1 566 920 598

所以,在借鉴(白嫖)了很多大佬的方法之后,我总结出来一下一种方法:

  • 动态规划

至于为什么只有一种,emmm,我看到的只有这一种

为什么是动态规划?
首先,我们要明白,每一行无论你怎么取都影响不到其他的行,所以动态规划实际上是在每一行的应用
我们可以把每一次拿走边缘的任意一个数的操作分为两种情况:

  • 拿左边
  • 拿右边

而剩下的区间可以重复如此操作,直到没有数取为止。
所以,倒推回来,我们可以看做:

  • 在数组中任取一个数,当前分值设为你选的数
  • 在你选择的数的左侧或右侧选一个数,使得当前分值*2 + 所选数的值最大
  • 当前分值则更新为选取的区间的最大分值(即初始数*2+所选的数
  • 重复2,3步骤直到没有数可以选

此时,我们可以开始规划如何动态规划了:
声明数组f[MAXN][MAXN],使f[i][j]表示,在当前数组中,第i个数到第j个数(闭区间)可以选到的最大分值
而初始值,则有f[i][i] = nums[i],只选自己
这样,可以依据上述步骤推出状态转移方程:
f[i][j] = max(nums[i] + f[i + 1][j], nums[j] + nums[i][j - 1]
即选左边或者右边的最大值
而看数据,还是有点小大,需要用到高精度,但是怎么算最大位数……反正不会超过2^128,就定40位好了。

定义高精度的类名我本来想用gj,但是转念一想,这不是gaoji的缩写吗,还是用ha (high accuracy)要好很多。

那么最初代码如下:

/** Author: Jeefy Fu* Email: jeefy163@163.com* Description:*        Origin URL: https://www.luogu.com.cn/problem/P1005*/#include <iostream>
#include <cmath>
#include <fstream>
#include <sstream>
#include <vector>
#include <array>
#include <string>
#include <algorithm>
#include <cstring>using namespace std;// high accuracy
// 高精度类
class ha {public:int n[41];// 构造器,创建时清零,防止出现乱七八糟的东西ha() {memset(n, 0, sizeof(n));}// 主要是为了把int转换为高精度数ha add(int b) {ha r;r.n[0] = b;int i = 0;while (r.n[i] != 0) {r.n[i] += n[i];r.n[i + 1] += r.n[i] / 10;r.n[i++] %= 10;}return r;}ha add(ha b) {ha r;for (int i = 0; i < 40; i++) {r.n[i] += n[i] + b.n[i];r.n[i + 1] += r.n[i] / 10;r.n[i] %= 10;}return r;}ha mul(int x) {ha r;for (int i = 0; i < 40; i++) {r.n[i] = n[i] * x;}for (int i = 0; i < 40; i++) {r.n[i + 1] += r.n[i] / 10;r.n[i] %= 10;}return r;}// 默认参数,不要在意这个语法void print(bool newline = true) {int i = 40;while (n[--i] == 0 && i > 0);while (i >= 0)putchar(n[i--] + '0');if (newline) putchar('\n');}
};// 重载max对于高精度数的函数
ha max(ha a, ha b) {for (int i = 40; i >= 0; i--) {if (a.n[i] > b.n[i]) return a;else if (a.n[i] < b.n[i]) return b;}return a;
}ha nums[80], result;ha lineMax(ha * ns, int m) {// 使用static数组,减少空间创建与释放static ha dp[80][80];// 由于使用的是static数组,所以每一次也要清零才能继续memset(dp, 0, sizeof(dp));for (int i = 0; i < m; i++) {dp[i][i] = dp[i][i].add(ns[i]);// printf("DP[%d][%d] set to: ", i, i); dp[i][i].print();}// 思考:为什么要这么写循环?// 答案可以参考后面的优化for (int len = 2; len <= m; len++) {for (int i = 0, j = len - 1; j < m; i++, j++) {// 这么多printf是用来调试的,请勿在意// printf("At DP[%d][%d]:\n", i, j);// printf("\tns[%d]: ", i); ns[i].print(false);// printf("  ns[%d]: ", j); ns[j].print();// 分两行写好看一点^_^dp[i][j] = max(ns[i].add(dp[i + 1][j].mul(2)), ns[j].add(dp[i][j - 1].mul(2)));// printf("\tDP[%d][%d] set to: ", i, j); dp[i][j].print();}}return dp[0][m - 1].mul(2);
}int main() {int n, m;cin >> n >> m;ha result;for (int i = 0; i < n; i++) {// 每一都要清零memset(nums, 0, sizeof(nums));int tmp;for (int j = 0; j < m; j++) {cin >> tmp;nums[j] = nums[j].add(tmp);}ha lm = lineMax(nums, m);// printf("line %d add max: ", i); lm.print();result = result.add(lm);}result.print();return 0;
}

优化

这里优化一下空间复杂度
如果我们把dp数组的变化给呈现出来,就会发现有一半的空间是没有使用的。
拿样例的第二行1 2 3举例
最终dp数组应该是这样呈现的:

横是i,竖是j

i0 i1 i2
j0 1
j1 5 2
j2 17 8 3

最终答案是17 * 2 = 34
我们不难发现,算完(i0, j1),(i0, j0)也就没有什么用了,所以可以压缩一下空间:

其他的代码不变,啊,这就是模块化编程的魅力所在

ha lineMax(ha * ns, int m) {ha * dp = new ha[m];for (int i = 0; i < m; i++) {dp[i] = dp[i].add(ns[i]);}for (int len = 1; len < m; len++) {for (int i = 0; i < m - i; i++)dp[i] = max(ns[i].add(dp[i + 1].mul(2)),ns[i + len].add(dp[i].mul(2)));}return dp[0].mul(2);
}

完事,下课

洛谷 P1005 矩阵取数游戏相关推荐

  1. 洛谷P1005 矩阵取数游戏 ACM 大数+区间dp

    题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n \times mn×m的矩阵,矩阵中的每个元素a_{i,j}ai,j​均为非负整数.游戏规则如下: 每次取数时须从每行各取走一个元素,共n ...

  2. 【每日DP】day 10、P1005 矩阵取数游戏【区间DP+高精(python)】难度⭐⭐⭐★

    P1005 矩阵取数游戏 输入 2 3 1 2 3 3 4 2 输出 82 说明/提示 NOIP 2007 提高第三题. 数据范围: 60%60\%60% 的数据满足:1≤n,m≤301\le n,m ...

  3. P1005 矩阵取数游戏(__int128模板/简单dp)

    转跳P1005 题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n \times mn×m 的矩阵,矩阵中的每个元素 a_{i,j}a i,j ​ 均为非负整数.游戏规则如下: 每次取数时 ...

  4. 【洛谷P1288】取数游戏II

    取数游戏II 题目链接 显然,由于一定有一个0,我们可以求出从初始点到0的链的长度 若有一条链长为奇数,则先手可以每次取完一条边上所有的数, 后手只能取另一条边的数,先手必胜: 反之若没有奇数链,后手 ...

  5. 洛谷1005 【NOIP2007】矩阵取数游戏

    问题描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2. ...

  6. 【每日一题】7月10日精讲—矩阵取数游戏

    来源:牛客网: 文章目录 题目描述 题解: 代码: 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld ...

  7. 【codevs1166】【noip07TG】矩阵取数游戏,新的开始

    1166 矩阵取数游戏 2007年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description [问题描述] 帅帅经 ...

  8. 【NOIP2007提高组】矩阵取数游戏

    题目背景 NOIP2007提高组试题3. 题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n*m 的矩阵,矩阵中的每个元素 aij 均为非负整数.游戏规则如下: 1.每次取数时须从每行各取 ...

  9. 矩阵游戏java_矩阵取数游戏JAVA题解

    话不多说,先上题目: 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的nm的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: {C}1. 每次取数时须从每行各取走一个元素,共n个.m次后取完矩 ...

  10. 矩阵取数游戏(动态规划)

    小明经常玩一个矩阵取数游戏:对于一个给定的n x n的矩阵,矩阵中的每个元素aij表示该格子的价值,均为非负整数.游戏规则如下: 小明从左上角走到右下角,只能向下向右走,经过某个格子,就能获得格子相应 ...

最新文章

  1. jquery 1.9里面已经删除了toggle(fn1, fn2)函数:
  2. 关于jQuery中的trigger和triggerHandler方法的使用
  3. 单件模式(单例模式)
  4. centos7加入第二块网卡无法识别
  5. 记录qt窗口在拖动过程中出现的问题
  6. python网格搜索核函数_(转载)Python机器学习笔记GridSearchCV(网格搜索)
  7. 猪八戒网的DevOps进化论
  8. 东软软件动态生成对数据表更新操作的方法
  9. 【机器学习-西瓜书】三、线性回归;对数线性回归
  10. 如何让微博营销更具效力
  11. winrar注册码激活码
  12. Android从Assets复制文件到本地
  13. vue+elementUi——实现后台管理系统的布局(sideBar+header+appMain)
  14. 解决win10系统命令提示符添加路由时提示请求的操作需要提升问题
  15. html5画布获取位置,html5画布绘制位置不正确(html5 canvas drawing position not correct)
  16. ubuntu java 中文_Ubuntu下 JAVA GUI 程序汉字显示为方框解决办法
  17. 大脑神经网络具有什么性,神经网络跟大脑的关系
  18. linux系统如何设置程序开机自启动
  19. Popular Cows POJ 2186(强连通分解)
  20. 2018最新北风网人工智能

热门文章

  1. 计算机无法投影,win10专业版投影失败提示:你的电脑不能投影到其他屏幕的解决办法...
  2. 软件测试基本流程【车机测试】
  3. 曲线运动与万有引力公式_干货|曲线运动与万有引力定律相关解题技巧
  4. matlab脉冲压缩,雷达线性调频脉冲压缩的原理及其matlab仿真
  5. 浪潮服务器支持pcie ssd硬盘吗,PCI-E与SATA SSD如何选?一分钟看懂
  6. SDUT-3337 计算长方体、四棱锥的表面积和体积
  7. 硅谷华人码农成语大全
  8. ccs定义的函数不变色_ccs使用问题及解决办法
  9. 4.3.5 心跳和协调者的关系
  10. Idea修改主题,字体等常规操作