【NOIP 2007】矩阵取数游戏(区间DP)

一开始思路走偏 一直在想肯定与第几次取数有关 然后做了半天没做出来

看了题解才恍然大悟 原来有个性质“可以每行分开来做 最后加起来就好了”

那就变成了一个区间DP 我们设dp[i][j]表示从i到j闭区间的最大价值。(只考虑当前行)

那么dp[i][j]肯定是从i的右边/j的左边转移来的 则有dp[st][ed] = max( dp[st][ed-1] + num[ed] * 2^(m-len+1) , dp[st+1][ed] + num[st] * 2^(m-len+1));

然后答案就是dp[1][m] 最后把所有答案相加就好

多说一句 一般来说区间DP的写法都是先枚举区间长度 随后枚举起点

都8012年了 谁还会让你去写高精 果断__int128

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll __int128
#define N 85 
using namespace std;
int n,m,num[N];
ll bit[N],dp[N][N],ans;	//只用关心每一行  
void write(ll x)
{
    if(x==0) return;
    else if(x) write(x/10);
    putchar(x%10+'0');
}
inline void Solve()
{
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=m;i++)	dp[i][i]=num[i]*bit[m];
	for(int len=2;len<=m;len++)
	{
		for(int st=1;st+len-1<=m;st++)
		{
			const int ed=st+len-1;
			dp[st][ed]=max(dp[st][ed-1]+num[ed]*bit[m-len+1],dp[st+1][ed]+num[st]*bit[m-len+1]);
		}
	}
}
int main()
{
	cin>>n>>m;
	bit[0]=1;
	for(int i=1;i<=m;i++)	bit[i]=bit[i-1]*2;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>num[j];
		}
		Solve();
		ans+=dp[1][m];
	}
	if(ans==0)	cout<<"0"<<endl;
	else write(ans);
	return 0;
}
posted @ 2018-10-21 11:20  Patrickpwq  阅读(173)  评论(0编辑  收藏  举报