矩阵取数 LuoguP1005
思路
考虑实际上,对于每一行,我们的对答案的贡献是单独的,所以对于每一行单独考虑。然后,我们的决策有两种,一种是拿走最左边的,一种是拿走最右边的。这两种操作都会使区间减小 \(1\) 的大小。
考虑一个大区间在进行操作后会变为一个小区间,最后会剩下一个为一长度的区间,一个区间在选择它的前驱区间时,无后效性。所以考虑 DP。
设 \(dp_{i,j}\) 为剩 \(i,j\) 区间的最大对答案贡献值。所以最后的答案就是 \(\max\{dp_{i,i} + a_{i} \cdot 2^m\}\)。
状态转移方程:
\[\begin{aligned}
dp_{i,j}=\max(dp_{i-1,j}+a_{i}\cdot2^{m-(j-i+1)},dp_{i,j+1}+a_{j}\cdot2^{m-(j-i+1)})
\end{aligned}
\]
考虑到 \(2^{80}\) 过大,考虑 __int128,答案的最大值小于 \(2^{80}\times 1000^2\),是小于 \(2^{128}\) 的,直接用 __int128 就行,自己手写输出,注意特判等于 \(0\) 的情况。
Code
#include <bits/stdc++.h>
#define int __int128
#define sd signed
using namespace std;
const int N=90;
void putout(__int128 x) {
if(x==0) puts("0");
string s;
while(x) {
sd tmp=x%10;
s+=(tmp+'0');
x/=10;
}
reverse(s.begin(),s.end());
cout<<s;
}
int qmi(int a,int b) {
int res=1;
while(b) {
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int f[N][N],ans;
sd a[N][N],n,m;
sd main() {
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++) {
int maxn=0;
f[1][m]=0;
for(sd l=1;l<=m;l++)
for(sd r=m;r>=1;r--) {
if(l==1&&r==m) continue;
if(l!=1) f[l][r]=max(f[l][r],f[l-1][r]+(int)a[i][l-1]*qmi(2,m-(r-l+1)));
if(r!=m) f[l][r]=max(f[l][r],f[l][r+1]+(int)a[i][r+1]*qmi(2,m-(r-l+1)));
}
for(int j=1;j<=m;j++) if((f[j][j]+a[i][j]*qmi(2,m))>maxn) maxn=(f[j][j]+a[i][j]*qmi(2,m));
ans+=maxn;
memset(f,0,sizeof f);
}
putout(ans);
return 0;
}

浙公网安备 33010602011771号