题目描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n×m 的矩阵,矩阵中的每个元素 ai,j 均为非负整数。游戏规则如下:
- 每次取数时须从每行各取走一个元素,共 n 个。经过 m 次后取完矩阵内所有元素;
- 每次取走的各个元素只能是该元素所在行的行首或行尾;
- 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 * 2^i,其中 i 表示第 i 次取数(从 1 开始编号);
- 游戏结束总得分为 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行的相加的和。(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]=
注意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);
}