题目描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n∗mn*mn∗m的矩阵,矩阵中的每个元素aija_{ij}aij均为非负整数。游戏规则如下:
1.每次取数时须从每行各取走一个元素,共nnn个。mmm次后取完矩阵所有元素;
2.每次取走的各个元素只能是该元素所在行的行首或行尾;
3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值∗2i*2^i∗2i,其中iii表示第i次取数(从1开始编号);
4.游戏结束总得分为mmm次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
题目分析
采用区间型dp
状态转移方程:
f[i][i+len]f[i][i+len]f[i][i+len]表示取数iii到i+leni+leni+len的最大值
f[i][i+len]=max(2∗f[i+1][i+len]+2∗a[i],2∗f[i][i+len−1]+2∗a[i+len])f[i][i+len]=max(2*f[i+1][i+len]+2*a[i],2*f[i][i+len-1]+2*a[i+len])f[i][i+len]=max(2∗f[i+1][i+len]+2∗a[i],2∗f[i][i+len−1]+2∗a[i+len])
取左边和取右边的最大值,用类似于秦九韶算法代替2的乘方。请注意使用高精度或__int128int128int128
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define lll __int128
using namespace std;
int n,m,a[82]; lll f[82][82],ans;
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(lll ans){
if (!ans) return;
else print(ans/10);
putchar(ans%10+48);
}
lll dp(int *a){
memset(f,0,sizeof(f));
for (int len=0;len<=m;len++)
for (int i=1;i+len<=m;i++)
f[i][i+len]=max(2*f[i+1][i+len]+2*a[i],2*f[i][i+len-1]+2*a[i+len]);//动态规划
return f[1][m];
}
int main(){
n=in(); m=in();
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++) a[j]=in();
ans+=dp(a);
}
if (ans) print(ans); else putchar('0');
return 0;
}