【洛谷】P1005 矩阵取数游戏(动态规划)

题目链接

题目描述

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

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

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

输入输出格式

输入格式:

 

输入文件包括 n+1 行:

第 1 行为两个用空格隔开的整数 n 和 m 。

第 2~n+1 行为 n×m 矩阵,其中每行有 m 个用单个空格隔开的非负整数。

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制

2 3
1 2 3
3 4 2

输出样例#1: 复制

82

数据范围

60%的数据满足: 1≤n,m≤30 ,答案不超过 10^16
100%的数据满足:1≤n,m≤80 , 0≤ai,j​≤1000

 

dp[i][j]表示矩阵的一行在区间变成[i,j]后的最大得分值

则任意矩阵取数后的最大得分等于第1~n行的max_j_<_=_m (dp[j][j]+a[k][j]*2^{m})相加的和。(a[k][j]为矩阵中第k行第j列元素)

由①每次取走的各个元素只能是该元素所在行的行首或行尾

②每行取数的得分 = 被取走的元素值 * 2^i,其中 i 表示第 i 次取数(从 1 开始编号);

③区间为[i,j]时,剩余元素个数为:j-i+1,取数的编号是:m-(j-i+1)+1=m-j+i;

得第k行的状态转移方程为:dp[i][j]=max(dp[i-1][j]+a[k][i-1]*2^{m-j+i-1},dp[i][j+1]+a[k][j+1]*2^{m-j+i-1})

 

注意long long 精度不够,可以考虑用__int128,用__int128的话要写个输入函数和输出函数或者运算符重载。

 

AC代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
inline void input(__int128 &s)
{
	s=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		s=s*10+c-'0';
		c=getchar();
	}
        s=s*f;
}
inline void output(__int128 a)
{
	if(a>9) output(a/10);
	putchar(a%10+'0');
}
int n,m;
__int128 dp[1001][1001]; 
__int128 matrix[81];//取数的矩阵,一行一行的处理
__int128 pow_2[81]={1}; //用pow_2[i]表示2的i次方 
int main()
{
	cin>>n>>m;
	__int128 ans=0;//总最大得分值 
	for(int i=1;i<=80;i++) pow_2[i]=2*pow_2[i-1];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)	input(matrix[j]);
        
                memset(dp,0,sizeof(dp));
                for(int p=1;p<=m;p++)
                for(int q=m;q>=p;q--)
                dp[p][q]=max(dp[p-1][q]+matrix[p-1]*pow_2[m-q+p-1],dp[p][q+1]+matrix[q+1]*pow_2[m-q+p-1]);
		
		__int128 Max=-1;//每一行最大的得分值 
		for(int j=1;j<=m;j++) Max=max(Max,dp[j][j]+matrix[j]*pow_2[m]);
		ans+=Max;
	}
	output(ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值