题面
题目描述
帅帅经常更同学玩一个矩阵取数游戏:对于一个给定的
n∗m
n∗m
的矩阵,矩阵中的每个元素a[i][j]据为非负整数。游戏规则如下:
1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有的元素;
2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
3. 每次取数都有一个得分值,为每行取数的得分之和;每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);
游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入格式
包括n+1行;
第一行为两个用空格隔开的整数n和m。
第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开
输出格式
仅包含1行,为一个整数,即输入矩阵取数后的最大的分。
题解
区间dp水题。
每一行做一次区间dp。
然后注意取数的权值处理问题,也就是当前数是第几个取。
最小的区间f[j][j]是最后取得,权值是
2m
2
m
。
状态:
f[H][i][j]代表第H行区间[i,j]的最大收益
f
[
H
]
[
i
]
[
j
]
代
表
第
H
行
区
间
[
i
,
j
]
的
最
大
收
益
对于一个区间[i,j],最先取的数可能是a[i]或者a[j]。
所以状态转移方程是:
f[H][i][j]=max(f[H][i][j−1]+a[j]∗2m−len+1,f[H][i+1][j]+a[i]∗2m−len+1)
f
[
H
]
[
i
]
[
j
]
=
m
a
x
(
f
[
H
]
[
i
]
[
j
−
1
]
+
a
[
j
]
∗
2
m
−
l
e
n
+
1
,
f
[
H
]
[
i
+
1
]
[
j
]
+
a
[
i
]
∗
2
m
−
l
e
n
+
1
)
这里有一个骚操作——__int128 请自行百度
code
#include<bits/stdc++.h>
using namespace std;
typedef __int128 ll;
inline ll read(){
ll num = 0;
char c = ' ';
bool flag = true;
for(;c > '9' || c < '0';c = getchar())
if(c == '-')
flag = false;
for(;c >= '0' && c <= '9';num = num*10+c-48,c=getchar());
return flag ? num : -num;
}
void print(ll a){
if(a>9)print(a/10);
putchar(a%10+'0');
}
const ll maxn=90;
ll f[maxn][maxn][maxn];
ll n,m,a[maxn][maxn];
ll qpow[maxn];//存放2的幂。
void init(){
n=read();m=read();
qpow[0]=1;
for(int i=1;i<=m;i++)
qpow[i]=qpow[i-1]*2;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
a[i][j]=read();
f[i][j][j]=a[i][j]*qpow[m];
}
}
void DP(){
for(int H=1;H<=n;H++){
for(int len=2;len<=m;len++)
for(int i=1;i+len-1<=m;i++){
int j=i+len-1;
f[H][i][j]=max(f[H][i][j],f[H][i+1][j]+a[H][i]*qpow[m-len+1]);
f[H][i][j]=max(f[H][i][j],f[H][i][j-1]+a[H][j]*qpow[m-len+1]);
}
}
ll ans=0;
for(int i=1;i<=n;i++)
ans=ans+f[i][1][m];
print(ans);
}
int main(){
init();
DP();
return 0;
}