题解: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);
}

浙公网安备 33010602011771号