题面
前言
这题写高精多麻烦啊,一个 __int128 就可以完全把高精替换掉,代码还比高精的短。
然后,我就用 __int128 水过去了。(绝对不是我懒得写高精呢)。
题解
一句话题意 每次取数从每一行行首或行末取一个,每次取都有一定的价值,问你怎么取价值最大。
首先,每一行可以分开来考虑,我们最后的答案就是每一行最优结果相加。
那问题就转化为了,我们怎么在一行取使这一行价值最大。
大家都很容易想到区间 dp吧。
状态是 \(f[i][j]\) 表示这一行剩下从 \(i\) 到 \(j\),其他都被选上的最大价值
转移方程 \(f[i][j] = max(f[i-1][j]+a[k][j] \times 2^{m+i-j-1} , f[i][j+1] + a[k][j] \times 2^{m+i-j-1})\)
转移时要注意第一层是正序枚举的,但第二层却要倒序枚举。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
inline __int128 read()
{
__int128 s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
__int128 n,m,tot,ans,w[110][110],f[110][110],base[110];
void print(__int128 x)//手写输入函数
{
if(x>9)
{
print(x/10);
}
cout<<(int) (x%10);
}
int main()
{
n = read(); m = read(); base[0] = 1;
for(int i = 1; i <= m; i++) base[i] = base[i-1] * 2;//预处理出二的幂次方
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
w[i][j] = read();
}
}
for(int i = 1; i <= n; i++)
{
memset(f,0,sizeof(f)); tot = 0;//每做一行都要清空
for(int l = 1; l <= m; l++)
{
for(int r = m; r >= 1; r--)//转移
{
f[l][r] = max(f[l][r],f[l-1][r]+w[i][l-1]*base[m+l-r-1]);
f[l][r] = max(f[l][r],f[l][r+1]+w[i][r+1]*base[m+l-r-1]);
}
}
for(int j = 1; j <= m; j++) tot = max(tot,f[j][j]+w[i][j]*base[m]);
ans += tot;
}
print(ans);
return (int)0;
}