【20171026】Luogu P1108 低价购买
2017-10-26 17:43
218 查看
题目描述
“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价(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种方案被认为是相同的。
输入输出样例
输入样例#1:复制BUYLOW.IN 12 68 69 54 64 68 64 70 67 78 62 98 87
输出样例#1:复制
BUYLOW.OUT 4 2
不忙,
我们先定义几个量(初读可能很难理解,建议先向下看解题过程,再回头看量的定义):
a[i]表示 "原始序列的第[i]号元素",
d[i]表示 "以a[i]结尾的最长上升子序列的长度",
LIS[i]表示 "以a[i]结尾的最长上升子序列",
没看错,它是个序列恩,即LIS[i]={x,y,z,...,a[i]},其中x<y<z<...<a[i];
c[i]表示 "以a[i]结尾的LIS方案数"(依题意,方案不可重复计数),
现在看题!
对于第一问——最长长度:
求最长上升子序列LIS(Longest Increasing Subsequence)的dp方程:
上升!上升!上升!上天~~
则d[i]=max(d[j]+1),j∈[1,n-1] && a[j]<a[i],
表示对于每一个a[i],它都可以接到每一个a[j](保证a[j]<a[i])之后,长度加一,成为一个上升子序列;
所以ans1=
="(每种长度 )中的(最长者)”
="(每种"部分情况中最长长度")中的(最长者)"
="(每个d[i])中的(最长者)"
=max(d[i]),i∈[1,n].②
However,题目要求我们求最长下降子序列(我喜欢叫ta LDS(Longest Decreasing Subsequence) ),
下降!下降!下降!啪~~
则d'[i]=max(1,d'[j]+1),j∈[1,n-1] && a[j]>a[i]; ①
ans=max(d'[i]),i∈[1,n].
对于第二问——统计方案数(这里直接对下降子序列求解):
则c[i]=sum(c[j]),j∈[1,n-1] && a[j]>a[i] && d[j]+1==d[i],③
其中条件 "d[j]+1=d[i]" 表示 "我的a[i]是紧接在LDS[j]末尾的"
但是,题目中要求 "去重" ,即 "包含同样方案的不同位置序列 视为同一种序列"
那么如果j<i && a[j]==a[i] && d[j]==d[i],则"c[i]代表的方案必定包含着c[j]代表的方案",
根据③可知,c[i]与c[j]将被重复计算,这是不允许发生的,
所以我们选择保留(排在后面的,包含c[j]的)c[i],使c[j]=0即可.
于是有c[j]=0,j∈[1,n-1] && a[j]==a[i] && d[j]==d[i],④
ans2=sum(c[i]), i∈[1,n] && d[i]=ans1⑤
综上所述,解决此题,只需联立①②③④⑤求解即可:
d[i] = max(d[j]+1), j∈[1,n-1] && a[j]>a[i],①
ans1 = max(d[i]), i∈[1,n],②
c[i] = sum(c[j]), j∈[1,n-1] && a[j]>a[i] && d[j]+1==d[i],③
c[j] = 0, j∈[1,n-1] && a[j]==a[i] && d[j]==d[i],④
ans2 = sum(c[i]), i∈[1,n] && d[i]=ans1,⑤
下面这代码求第一问没问题了,第二问还有错误,我回去改改
luogu53分代码如下——
code1:
/* d[i]= max(d[j]+1), j∈[1,n-1]&&a[j]>a[i],① ans1 = max(d[i]), i∈[1,n].② c[i]=sum(c[j]), j∈[1,n-1]&&a[j]>a[i]&&d[j]+1==d[i],③ c[j]=0, j∈[1,n-1]&&a[j]==a[i]&&d[j]==d[i],④ ans2=sum(c[i]), i∈[1,n]&&d[i]=ans1⑤ */ #include<stdio.h> #include<string.h> #define MAXN 5002 int n; int a[MAXN],d[MAXN],c[MAXN]; int main() { memset(a,0,sizeof(a)); memset(d,0,sizeof(d)); memset(c,0,sizeof(c)); /*我的老师说可以用这种方法来将数组置0*/ scanf("%d",&n); int i,j; int ans1,ans2; for(i=1;i<=n;i++) { scanf("%d",&a[i]); } ans1=ans2=0; for(i=1;i<=n;i++) { d[i]=1;//a[i]本身就可以是一个长度为 1 的LDS for(j=1;j<i;j++) { if(d[j]+1>d[i]&&a[j]>a[i]) d[i]=d[j]+1; } if(d[i]>ans1) ans1=d[i]; } for(i=1;i<=n;i++) { for(j=1;j<i;j++) { if(a[j]>a[i]&&d[j]+1==d[i]) c[i]+=c[j]; if(a[j]==a[i]&&d[j]==d[i]) c[j]=0; } if(d[i]==ans1) ans2+=c[i]; if(c[i]==0)//如果之前都没有LDS能以a[i]结尾的,那么就说明i自己是一个开头 c[i]=1;//这个初始化的摆放位置非常有内涵,值得思考哦 } printf("%d %d\n",ans1,ans2); }
错误处1(未考虑n==1的情况):code1第50~53行调一下位置,拿到了73分
code2:
if(c[i]==0)//如果之前都没有LDS能以a[i]结尾的,那么就说明i自己是一个开头 c[i]=1;//这个初始化的摆放位置非常有内涵,值得思考哦 if(d[i]==ans1) ans2+=c[i];