您的位置:首页 > 其它

【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];
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: