http://poj.org/problem?id=3186
类似的区间dp: https://blog.csdn.net/qq_32259423/article/details/89221957
题意:
给你n个数字v(1),v(2),...,v(n-1),v(n),每次你可以取出最左端的数字或者取出最右端的数字,一共取n次取完。假设你第i次取的数字是x,你可以获得i*x的价值。你需要规划取数顺序,使获得的总价值之和最大。
思路:
考虑区间dp, dp[i][j]表示最后取从i到j之间的数时,这些数能贡献的最大价值.
递推式: dp[i][j]=max(a[i]*(n-j+i)+dp[i+1][j],a[j]*(n-j+i)+dp[i][j-1])
其中, max里第一项是先取i数字(头)的情况, 第二项是先取j数字(尾)的情况
以第一项为例, 从i+1到j共用掉n,n-1,n-2,...,n-j+i+1这些乘数, 那么a[i]的乘数就是n-j+i了.
for的顺序: 先让区间长度短的计算完毕, 才能算长的, 所以外层枚举区间长度,内层枚举起点即可.
代码:
//#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fuck(x) std::cout<<"["<<#x<<"->"<<x<<"]"<<endl;
using namespace std;
typedef long long ll;
const int M=2e5+5;
const int inf=1e9+5;
const int mod=1e9+7;
//memset(a,0x3f,sizeof(a));
int dp[2005][2005];
int a[2005];
int main() {
memset(dp,0,sizeof(dp));
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
}
for(int ii=0; ii<=n; ii++) {
for(int i=1; i+ii<=n; i++) {
int j=ii+i;
if(i==j)
dp[i][j]=a[i]*(n-j+i);
else {
if(i!=n)
dp[i][j]=max(dp[i][j],a[i]*(n-j+i)+dp[i+1][j]);
if(j!=0)
dp[i][j]=max(dp[i][j],a[j]*(n-j+i)+dp[i][j-1]);
}
}
}
printf("%d\n",dp[1][n]);
return 0;
}