题目描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n ∗ m n*m n∗m的矩阵,矩阵中的每个元素 a i j a_{ij} aij均为非负整数。游戏规则如下:
1.每次取数时须从每行各取走一个元素,共 n n n个。 m m m次后取完矩阵所有元素;
2.每次取走的各个元素只能是该元素所在行的行首或行尾;
3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 ∗ 2 i *2^i ∗2i,其中 i i i表示第i次取数(从1开始编号);
4.游戏结束总得分为 m m m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
题目分析
采用区间型dp
状态转移方程:
f
[
i
]
[
i
+
l
e
n
]
f[i][i+len]
f[i][i+len]表示取数
i
i
i到
i
+
l
e
n
i+len
i+len的最大值
f
[
i
]
[
i
+
l
e
n
]
=
m
a
x
(
2
∗
f
[
i
+
1
]
[
i
+
l
e
n
]
+
2
∗
a
[
i
]
,
2
∗
f
[
i
]
[
i
+
l
e
n
−
1
]
+
2
∗
a
[
i
+
l
e
n
]
)
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的乘方。请注意使用高精度或__
i
n
t
128
int128
int128
代码
#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;
}