Description
“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价(216范围内的正整数),你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。
Analysis
拆解成两个问题:
- 求出最长下降子序列的长度
- 统计所有不同的最长下降子序列
最长下降子序列长度非常好求,难点主要在统计子序列个数。
不难想到:将一点视为子序列的末尾时,其所有组合的倒数第二位一定在数列中以升序排列。只要从头开始找到子序列中拥有最长上升子序列的不重复数字并加上其子方案数。
可以设置数字[n+1]为0,那么dp[n+1]-1就是最长下降子序列长度,sum[n+1]就是所有方案统计。
注意一旦出现子序列长度相同,数字相同节点,后面的方案数可能比前面更多,所以最好从后往前找。
边界非常重要,sum[]对于任何位置都保底为1。
Code
#include <bits/stdc++.h>
#define MAXN 50000
int n,num[MAXN],dp[MAXN],sum[MAXN];
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
std::cin>>n;
for(int i=0;i<n;i++){
std::cin>>num[i];
dp[i]=1;
}
num[n++]=0;
for(int i=0;i<n;i++){
for(int j=0;j<i;j++)
if(num[i]<num[j])
dp[i]=std::max(dp[i],dp[j]+1);
int mark=num[i];
for(int j=i-1;j>=0;j--)
if(num[i]<num[j]&&dp[j]+1==dp[i]&&num[j]!=mark){
sum[i]+=sum[j];
mark=num[j];
}
sum[i]=(!sum[i])?1:sum[i];
}
std::cout<<dp[n-1]-1<<" "<<sum[n-1]<<std::endl;
return 0;
}