题解:P2010 [NOIP2007 提高组] 矩阵取数游戏

做法

每次取数时,必须从每一行各取一个数(要么行首,要么行尾)。这意味着,对于每一行来说,取数的顺序(即选择行首还是行尾)只影响当前行的得分,而不会影响其他行的取数情况。从而得到每行是独立的,我们考虑拆开取做。

对于第 \(i\) 行,显然有一个区间 DP 的做法,定义 \(f_{i,j}\) 表示 \(i\)\(j\) 这个区间可以取得的最大值,初始化的话就是 \(f_{i,i}=a_{k,i}\times 2\),其中 \(k\) 是当前到了第几行。

然后考虑转移,为了方便,我先是用快速幂求出了 \(2^{m-{i-j+1}+1}\)\(i-j+1\) 是区间长度,所以当前次数就是 \(m-{i-j+1}+1\) 了,记为 \(cnt\)

转移有两边:

  • \(f_{i+1,j}\) 转移,\([i,j]\) 这个区间比 \([i+1,j]\) 就多了一个数 \(a_{k,i}\) 所以转移要带上它,得到 \(f_{i+1,j}+a_{k,i}\times cnt\)
  • \(f_{i,j-1}\) 转移,\([i,j]\)\([i,j-1]\) 多了 \(a_{k,j}\),所以转移就是 \(f_{i,j-1}+a_{k,j}\times cnt\)

但是显然不可以两边同时转移,需要取一个 \(\max\)

对于第 \(k\) 行的最优答案就是 \(f_{1,m}\) 惹,每行的全部加起来可以就是我们的答案。

本题卡精度,至少要是 __int128

最后警示你一下:如果你 __int128 都挂了,就写高精度吧,可能是你的代码有一些点爆了,处理不够优秀。

CODE

#include<bits/stdc++.h>
#define int __int128
using namespace std;
int f[85][85],a[85][85];
inline int read(){
	int x=0,f=1;
	char ch=getchar_unlocked();
	while (!isdigit(ch)){
		if (ch=='-') 
			f=-1;
		ch=getchar_unlocked();
	}
	while (isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar_unlocked();
	}
	return x*f;
}
inline void write(int x){
	if (x<0)putchar('-'),x=-x;
	if (x>9)write(x/10);
	putchar(x%10+'0');
}
int qpow(int a,int b){
    int ans=1;
    while (b){
        if (b&1)ans*=a;
        a*=a;
        b>>=1;
    }
    return ans;
}
signed main(){
    int n,m;n=read(),m=read();
    for(int i=1;i<=n;++i){
        for (int j=1;j<=m;++j){
            a[i][j]=read();
        }
    }
    int ans=0;
    for (int k=1;k<=n;++k){
        memset(f,0,sizeof f);
        for (int len=0;len<m;++len){
            for (int i=1;i+len<=m;++i){
                int j=i+len;
                int cnt=qpow(2,m-(j-i+1)+1);
                if (i==j){
                    f[i][j]=a[k][i]*cnt;
                }
                else{
                    f[i][j]=max(f[i+1][j]+a[k][i]*cnt,f[i][j-1]+a[k][j]*cnt);
                }
            }
        }
        ans+=f[1][m];
    }
    write(ans);
}
posted @ 2025-07-16 10:36  Cefgskol  阅读(18)  评论(0)    收藏  举报