动态规划基础题:低价购买(最长下降子序列)

来源:互联网 发布:淘宝闲鱼拍卖 编辑:程序博客网 时间:2024/05/22 13:36

题目描述

“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价(2^16范围内的正整数),你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。

这里是某支股票的价格清单:

日期 1 2 3 4 5 6 7 8 9 10 11 12

价格 68 69 54 64 68 64 70 67 78 62 98 87

最优秀的投资者可以购买最多4次股票,可行方案中的一种是:

日期 2 5 6 10

价格 69 68 64 62

输入输出格式

输入格式:
第1行: N (1 <= N <= 5000),股票发行天数

第2行: N个数,是每天的股票价格。

输出格式:
输出文件仅一行包含两个数:最大购买次数和拥有最大购买次数的方案数(<=2^31)当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这2种方案被认为是相同的。


看完试题就应该能明白,这是一道典型的最长下降子序列问题。最长下降子序列是一个典型的O(n^2)动态规划问题。本文将阐述如何求出最长下降子序列长度、统计个数以及快速去重。


长度求解
其实就是一个很简单的转移问题。对于第i个元素,它的最长下降子序列长度总是由前i-1个元素转移得来的。具体而言,它的转移总来源于前I-1个元素中满足a[i]>a[j] (j∈[1,i-1])的最长下降子序列长度最大的元素。听起来很绕口,我们用转移方程来表示:
f[i]=f[j]+1 (j∈[1,i-1]且j∈N*, i∈[1,n], a[i]

#include <cstdio>#define max(x,y) x>y?x:yusing namespace std;int n,a[5005],f[5005];int main(){    int i,j,ans=0;    scanf("%d",&n);    for(i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=1;    for(i=2,j=1;i<=n;j=j==i-1?1:j+1,i+=(j==1))        if(a[i]<a[j]&&f[j]>=f[i]) f[i]=f[j]+1;    for(i=1;i<=n;i++) ans=max(ans,f[i]);    printf("%d",ans);    return 0;}

统计个数
如何进行个数统计?看起来这个问题很简单,只要开一个数组,在转移的时候不断修改就行了。事实上,真正写起来,要做的修改还是挺多的。

#include <cstdio>#define max(x,y) x>y?x:yusing namespace std;int n,a[5005],f[5005],c[5005];int main(){    int i,j,ans=0;    scanf("%d",&n);a[n+1]=-1;    for(i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=1,c[i]=1;    for(i=2,j=1;i<=n+1;j=j==i-1?1:j+1,i+=(j==1)){        if(a[i]<a[j]&&f[i]==f[j]+1) c[i]+=c[j];        if(a[i]<a[j]&&f[j]>=f[i]) f[i]=f[j]+1,c[i]=c[j];    }    printf("%d %d\n",f[n+1]-1,c[n+1]);    return 0;}

输入
4
4 4 2 1
输出
3 2
哈!统计成功了……慢着,好像有哪里不对……哪里来的两个序列?不都是4 4 2 1吗?


快速去重
刚刚我们看到,会有重复的序列来影响计数,也就是题目描述的“看起来一样”。因此,我们需要一种去重方法。
有人说:去重简单!把所有的方案记录下来,然后挨个比较不就好了?
行!只是……真的很慢。

那么我们不妨换一种思维……由于重复只会影响计数数组c[i],并且重复现象的作用一定是产生在c[j]到c[i]的转移过程中的。不如,我们从源头出发,来阻止这个问题的发生。
在每一次进行计数后,开辟一次O(i)的校验,当且仅当存在v[i]==v[j],f[i]==f[j]时,将c[i]强制设置为0.

#include <cstdio>#define max(x,y) x>y?x:yusing namespace std;int n,a[5005],f[5005],c[5005];int main(){    int i,j,ans=0;    scanf("%d",&n);a[n+1]=-1;    for(i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=1,c[i]=1;    for(i=2,j=1;i<=n+1;j=j==i-1?1:j+1,i+=(j==1)){        if(a[i]<a[j]&&f[i]==f[j]+1) c[i]+=c[j];        else if(a[j]==a[i]&&f[i]==f[j]) c[j]=0;        if(a[i]<a[j]&&f[j]>=f[i]) f[i]=f[j]+1,c[i]=c[j];    }    printf("%d %d\n",f[n+1]-1,c[n+1]);    return 0;}
0 0
原创粉丝点击