*题目链接http://codevs.cn/problem/1166/
因为剩下的40%是高精度而且是恶心的高精度,我就先打60%吧。。。
一开始想的转移方程错了,调了一会发现不满足无后效性原则,一开始的想法是这样的:
dp[i][j]是第i次取数,取的数是第j个的最大的答案,枚举第i-1次取的数,但是这样做不能满足j之前没有被取过,因此不可以
正确的转移方程:
对于每一行:
dp[i][j]表示剩下区间[i,j]的数时的最大值。为什么要这样呢?
因为我们发现每次取走首或尾的两个数时,剩下的数一定是一个区间,我们的每个区间的答案可以由更小的区间转移过来,因此可以从小到大枚举区间长度求解
这里有一个比较巧妙的地方,就是我们每次增加一个区间长度,前一个区间的值乘以2,初始是把dp[i][i]设为ju[i],m次后,区间长度为1的答案乘以了2^(m-1),长度为2的乘以了2^(m-1),……区间长度为m的乘以了2^0,最后的答案再把dp[1][m]*2就可以了
即:
dp[i][j]=max(dp[i+1][j]*2+ju[i],dp[i][j-1]*2+ju[j]);
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
typedef unsigned long long ll;
using namespace std;
const int maxn=100;
ll n,m,ans,ma;
ll dp[maxn][maxn],ju[maxn];
int main()
{
cin>>n>>m;
for(int x=1;x<=n;++x)
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;++i)
{
cin>>ju[i];
dp[i][i]=ju[i];
}
for(int i=2;i<=m;++i)//枚举区间长度
for(int j=1;j<=m-i+1;++j)//枚举起点
{
int l=j+i-1;
dp[j][l]=max(dp[j+1][l]*2+ju[j],dp[j][l-1]*2+ju[l]);
}
ans+=2*dp[1][m];
}
cout<<ans;
return 0;
}