矩阵取数 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;
}
posted @ 2024-08-24 15:04  PM_pro  阅读(11)  评论(0)    收藏  举报