约翰经常给产奶量高的奶牛发特殊津贴,于是很快奶牛们拥有了大笔不知该怎么花的钱.为此,约翰购置了N(1≤N≤2000)份美味的零食来卖给奶牛们.每天约翰售出一份零食.当然约翰希望这些零食全部售出后能得到最大的收益.这些零食有以下这些有趣的特性:
•零食按照1..N编号,它们被排成一列放在一个很长的盒子里.盒子的两端都有开口,约翰每
天可以从盒子的任一端取出最外面的一个.
•与美酒与好吃的奶酪相似,这些零食储存得越久就越好吃.当然,这样约翰就可以把它们卖出更高的价钱.
•每份零食的初始价值不一定相同.约翰进货时,第i份零食的初始价值为Vi(1≤Vi≤1000).
•第i份零食如果在被买进后的第a天出售,则它的售价是vi×a.
Vi的是从盒子顶端往下的第i份零食的初始价值.约翰告诉了你所有零食的初始价值,并希望你能帮他计算一下,在这些零食全被卖出后,他最多能得到多少钱.
输入输出格式
输入格式:
Line 1: A single integer, N
Lines 2..N+1: Line i+1 contains the value of treat v(i)
输出格式:
Line 1: The maximum revenue FJ can achieve by selling the treats
输入输出样例
5 1 3 1 5 2
43
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<algorithm>
#include<fstream>
usingnamespacestd;
int dp[2005][2005],a[2005];
int main(){
int n,l,i,j;
cin>>n;
for(i=1;i<=n;i++){
cin>>a[i];
dp[i][i]=a[i]*n;//初始化:将区间长度为1的情况赋值为最后一个拿(n*a[i])
}
for(l=2;l<=n;l++){//枚举长度
for(i=1;(i+l-1)<=n;i++){//枚举开头点,注意范围(开头点加长度减一不超过总长度)
j=i+l-1;//推出结束点
dp[i][j]=max(dp[i+1][j]+a[i]*(n-l+1),dp[i][j-1]+a[j]*(n-l+1));//有两种情况一种是选开头点的那一个最优,一种是选结束点的那一个最优
}
}
cout<<dp[1][n]<<endl;//最后直接输出从开头到末尾的最大值
return0;
}
第一个问题:为什么要dp[i][i]=a[i]*n; 为什么这样初始化!
第二个问题;为什么区间长度是从2开始的,我自己也推出形式相同的状态转移方程,但是我的区间长度是从n开始的,因为我觉得应该是两边向中间。
还有:有没有大佬指点一下,怎么才能自己推导动态转移方程啊·,我还刚开始做这块儿,我现在的状态是们基本上动态转移方程都必须看别人的,所以有点儿迷茫,
因为最好是自己推导。
问题一:dp[i][i]表示从i开始到i结束这个区间(也就是出现这种状态时),它的最大值。
问题二:因为问题一已经计算出区间长度为1的情况,所以要从2开始了。当计算出长度为n的区间的最大值时,也就是问题的解了。
为了理解动态规划,必须先理解它的几个性质,特别是最优子结构,也就是状态是怎么划分的,最好画个图。
另外也可以先写递归再写递推,毕竟递归更好理解。
怎么画图呢?有没有具体过程。大佬,可不可以推荐本书或则博客之类的。
@神韵袖藏: 才看清你的问题二,你从n开始就是递归,从2(其实是1)开始就是递推,都是对的(大概就是两边向中间,和中间向两边的区别吧,事实上只有中间向两边,因为只有计算出了局部才能计算全局)。
感觉被我说的更复杂了,推荐看看各种背包问题吧,网上很多。