題目來源:http://poj.org/problem?id=3186
(http://www.fjutacm.com/Problem.jsp?pid=1389)
/**
題目意思:
約翰經常給產奶量高的奶牛發特殊津貼,於是很快奶牛們擁有了大筆不知該怎麼花的錢.
爲此,約翰購置了N(1≤N≤2000)份美味的零食來賣給奶牛們.每天約翰售出一份零食.
當然約翰希望這些零食全部售出後能得到最大的收益.這些零食有以下這些有趣的特性:
零食按照1..N編號,它們被排成一列放在一個很長的盒子裏.盒子的兩端都有開口,約翰每
天可以從盒子的任一端取出最外面的一個.
與美酒與好吃的奶酪相似,這些零食儲存得越久就越好吃.當然,這樣約翰就可以把它們賣出更高的價錢.
每份零食的初始價值不一定相同.約翰進貨時,第i份零食的初始價值爲Vi(1≤Vi≤1000).
第i份零食如果在被買進後的第a天出售,則它的售價是vi×a.
Vi的是從盒子頂端往下的第i份零食的初始價值.約翰告訴了你所有零食的初始價值,
並希望你能幫他計算一下,在這些零食全被賣出後,他最多能得到多少錢.
**/
思路:這是一題區間動態規劃,但是又不是那麼複雜的區間動態規劃;
這題要先找對狀態,不然很難進行下去,也許別的小夥伴有別的狀態,但是我找的狀態是dp[i][j]表示i~j範圍內取出倒數j-i+1個物品的最大值,比如dp[i][i]就是第取出倒數第一個爲a[i]可以獲得的值,dp[i-1][i]同理,表示倒數第一、二個爲a[i-1],a[i]的最大值。
然後找對狀態了要找轉移方程:對於這題先要搞清楚我們是逆推,從區間小推向區間大,也就是用小的求大的,所以dp[i][j]是由比他小的部分加上一些東西組成的,此時我們可以發現,dp[i][j]這個區間我們只有兩種取法(題目給的是一個雙端隊列一樣的東西),也就是我們只能取a[j]或者a[i]這兩個數,而且這兩個數一定是在倒數j-i次取的,因爲之前只有j-i- 1(可能是a[i]或a[j]) 個數,而且都是倒數着的,這樣我們就可以發現,dp[i][j]=Max(dp[i+1][j]+a[i]*(n-(j-i)), dp[i][j-1]+a[j]*(n-(j-i)));看下面這個圖你就能理解他是怎麼步步緊逼的合併狀態的:
斜着的顏色分層的部分就表示i、j距離不同時由某些狀態過來的最大值,餘下的看看代碼吧!
ps:這裏這個距離是指帶代碼裏的l,因爲你要按照那個斜着的一層一層狀態轉移上去,否則就錯了;
#include<stdio.h>
#include<string.h>
const int N=2020;
int dp[N][N], a[N];
int Max(int a, int b){ return a>b?a:b; }
int main( ){
int n;
while(scanf("%d", &n)!=EOF){
memset(dp, 0, sizeof(dp));
for(register int i=1; i<=n; ++i)
scanf("%d", a+i), dp[i][i]=a[i]*n;///設置爲當第i個數爲最後一個被取的數的情況;
for(register int l=1; l<=n; ++l){///先跑i、j距離爲1的區間,然後順延下去纔可以保證不會出錯
for(register int i=1; i+l<=n; ++i){
int j=i+l;
dp[i][j]=Max(dp[i+1][j]+a[i]*(n-l), dp[i][j-1]+a[j]*(n-l));
}
}
printf("%d\n", dp[1][n]);
}
}