题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1087
题目大意:
就是给你一个数字序列,让你寻找一个从小到大排序的子序列是所有这样排序的子序列最大值。比如:下图所示中的,前面的第一个数代表着后面有几个数字。故第一例的数字序列就只是:1 3 2.所以其最大的子序列的值为4,这个子序列为:1 3;
故第二例:1 2 3 4,其最大子序列的值为10,子序列为1 2 3 4
思路:
这个需要用到dp数组来记录每个位置所代表着这个数字作为末尾值之前所能取的最大值最大序列。
比如:1,2,3,4。那么它们的dp值分别为:dp[0]=1,dp[1]=1+2=3,dp[2]=1+2+3,dp[3]=1+2+3+4
相信大家也发现了,这玩意儿可以写成,
dp[0] = 0 ,dp[1] = dp[0]+2,dp[2] = dp[1]+3,dp[3] = dp[2] + 4
如果1,2,3,4这个值在最开始就用数组a存起来的话,又会发现
dp[0]=a[0],dp[1]=dp[0]+a[1],dp[2]=dp[1]+a[2],dp[3] = dp[2]+a[3]
所以设置第n个dp的值可以写成:dp[n] = d[n-1] + a[n]
当然这个表达式只是针对这种少数情况的
如果在这之前有比a[n]大的数嘞,那样的话就会导致dp[n-1]找错了dp值了
所以需要遍历n的前n-1个数,找到比a[n]小的数(记这个数为a[k]),然后嘞,我们的表达式就可以写成dp[n] = dp[k]+a[n]
但是我们可以注意到,比如是:1 2 3 2 4的话
*************************(下标:0 1 2 3 4)
我们填dp[4]的时候会发现,最终会变成dp[4] = dp[3] + a[4]=3+4=7(这个式子是因为要循环遍历前n-1个找a[k]<a[n],所以最后一个就是为k=3,就是dp[3]咯)
然而,真正的dp[4]=1+2+3+4=dp[2]+a[4]=10
我们发现单独只是dp[n]=dp[k] + a[n]会导致被覆盖掉
所以写成:dp[n] = max{dp[n],dp[k] + a[n]}。令其算出的值每次都与自己进行比较下,比我大,我才写上去。这样的话就不会出现上面被覆盖掉的情况了。
ac代码:
#include<stdio.h>
typedef long long ll;
#define M 1005
ll max(ll a,ll b){
return (a>b)?a:b;
}
int main(){
int a[M];
int n;
int i;
ll dp[M];//记录当前点的总值(最优值)
ll ans;//记录最终的答案
while(scanf("%d",&n) != EOF){
//dp(n) = max(dp(n),dp(k)+a(n)); a[k]<a[j]
i = 0;
if(n == 0) break;
ans = 0;
while(n--){
scanf("%d",&a[i]);
i++;
}
dp[0] = a[0];
for(int j = 1;j < i;++j){
dp[j] = a[j];
for(int k = 0;k < j;k++){//遍历j的前面元素
if(a[k] < a[j]){//如果找到比j元素的值小的元素,就更新dp数组的值,
//判断依据为 dp(n) = max(dp(n),dp(k)+a(n));
//这样就能够保证这个循环过后这个dp[j]的值就是其这个点所能遍历的最大值
//如:1234 j = 2时,那么当k=0时也就是a[k]=1,a[j]=3,dp会更新为dp[2]=1+3;
//对应数组下标0123
//然后当k=1时,也就是a[k]=2,a[j]=3,dp会更新为dp[2]=2+3;
//所以最终的3这个位置所代表的最大的值为dp[2]=5;
dp[j] = max(dp[j],dp[k]+a[j]);
}
}
if(dp[j]>ans){
ans = dp[j];
}
}
printf("%lld\n",ans);
}
return 0;
}