矩阵取数游戏——典型的动态规划问题,值得一看

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

题目描述

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

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

样例输入 #1

2 3
1 2 3
3 4 2

样例输出 #1

82

提示

【数据范围】

对于 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}\le1000 0ai,j1000

【题目来源】

NOIP 2007 提高第三题。

思路

该题看上去是一个平面类问题,但是实际上不是,因为行与行之间的取数没有关系,我们只需要考虑每一行的取数即可。阶段按每次取数来划分。状态:取数之后这一行剩下的情况是不确定了,为了记录当前这一行的状态,我们用dp[i][j]表示取数取到还剩下[i,j]的最优解。状态转移:每次取数可以在左端点,也可以在右端点,如果在左端点取数,那么由dp[i-1][j]转移过来,如果在右端点取数,有dp[i][j+1]转移过来。
这里很明显有问题了,线性DP的做法是按照时间或者空间的先后顺序来求解,并且由已知量推到未知量,但是无论是从左到右还是从右刀座,左右两边的状态一定有一个未知量。该题其实是一道区间DP的题,取数是按照从少到多的顺序而不是按照从前往后的顺序,即序列长度的大小关系。所以我们在枚举区间左端点时从小到大枚举,枚举区间有端点是从大到小枚举,这样就保证了每次状态转移时的状态都是已知的。当区间为[l,r]时,一共有m个数,当前取的是第m-(r-l+1)个数。
那么什么时候表示整个区间的数都取完了呢?区间[l,l]表示还有一个数,所以区间的数取完应该是[l,l-1],而任意一个点都有可能是最终状态。
该题还有更加简单的状态转移过程,取数从两边往中间取或者从中间往两边取其实效果是一样的,只是每次乘的系数(2^n)不一样。该题可以倒过来看,看做把一个长度为0的区间不停的往两边加数最终变成区间[1,m]。而系数问题相当于每次把之前dp[i][j]的值*2再计算。

至于本题的高精度算法,其实我们可以运用一个 _ _ i n t 128 \_\_int128 __int128来解决,而且现在的CCF也将曾经对 _ _ i n t 128 \_\_int128 __int128的封令给取消了,所以我们就可以肆无忌惮的使用啦!
只不过这里要注意一下, _ _ i n t 128 \_\_int128 __int128需要用快读快写来读入和输出

ACcode

#include<bits/stdc++.h>
using namespace std;

__int128 a[100][100],dp[100][100],num[100],maxn;

inline __int128 read(){
    __int128 x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
inline void write(__int128 x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
}

int main(){
	num[0]=1;
	for(int i=1;i<=80;i++){
		num[i]=num[i-1]*2;
	}
	int n,m;
	cin>>n>>m;
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++){
			a[i][j]=read();
		}
	}
	for (int k=1;k<=n;k++){
		memset(dp,0,sizeof(dp));
		for (int len=1;len<=m;len++){
			for (int i=1;i+len-1<=m;i++){
				int j=i+len-1;
				dp[i][j]=max(dp[i+1][j]+a[k][i]*num[m-len+1],dp[i][j-1]+a[k][j]*num[m-len+1]);
			}
		}
		maxn+=dp[1][m];
	}
	write(maxn);
	return 0;
}

发表了100篇文章,祭~~

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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、付费专栏及课程。

余额充值