矩阵取数游戏

一道很简单的区间dp,竟然是道蓝 赶快去水积分吧

读题后不难发现,不同行的取数决策并不影响其它行的答案,因此我们dp时只要搞出每行的最大值,加起来就是答案了

对于每一行,我们只可以取行头或者行末,取完后剩下的数必定还是一段连续区间,且很容易通过大区间的答案推出小区间答案,是区间dp没错了

不妨设\(dp[i][j]\)为取完后剩下的数区间为\([i,j]\)时,答案的最大值。易得转移方程\(dp[i][j]=max(dp[i+1][j]+2^{m-(j-i)}*v[i],dp[i][j+1]+2^{m-(j-i)*v[j]} )\)

但是这个dp只可以取到只剩一个数,还要再把每个\(dp[i][i]\)再算一遍,\(ans=max\){\(dp[i][i]+2^m*v[i]\)}

关于高精问题,建议用int128

贴码,有些细节码里讲

#include <bits/stdc++.h>
using namespace std;
const int N=100;
int n,m;
__int128 a[N][N],f[N][N];
template<class T>
inline void read(T &x){ 
	x=0;int w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
	for(;ch>='0'&&ch<='9';ch=getchar())
		x=(x<<3)+(x<<1)+(ch&15);
	x*=w; 
}
inline void write(__int128 x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
}
__int128 dp(__int128 sum[])
{
	memset(f,0,sizeof(f));
	int i,j;
	for(i=0;i<m;i++)//由于dp[i][j]是由大区间推出来的,所以先循环大区间
		for(j=1;i+j<=m;j++)
			f[j][i+j]=max(2*(f[j+1][i+j]+sum[j]),2*(f[j][i+j-1]+sum[i+j]));
	return f[1][m];
}
int main()
{
	read(n),read(m);
	int i,j;
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
			read(a[i][j]);
	__int128 ans=0;
	for(i=1;i<=n;i++)
		ans+=dp(a[i]);//分行分别计算
	write(ans);
	return 0;
}
posted @ 2022-06-27 16:35  羊扬羊  阅读(187)  评论(0编辑  收藏  举报