给定一段时间内股票的每日售价(正 16 位整数)。
你可以选择在任何一天购买股票。
每次你选择购买时,当前的股票价格必须严格低于你之前购买股票时的价格。
编写一个程序,确定你应该在哪些天购进股票,可以使得你能够购买股票的次数最大化。
例如,下面是一个股票价格时间表:
Day 1 2 3 4 5 6 7 8 9 10 11 12
Price 68 69 54 64 68 64 70 67 78 62 98 87
如果每次购买都必须遵循当前股票价格严格低于之前购买股票时的价格,那么投资者最多可以购买四次该股票。
买进方案之一为:
Day 2 5 6 10
Price 69 68 64 62
输入格式
第 1 行包含整数 N,表示给出的股票价格的天数。
第 2 至最后一行,共包含 N 个整数,每行 10 个,最后一行可能不够 10 个,表示 N 天的股票价格。
同一行数之间用空格隔开。
输出格式
输出占一行,包含两个整数,分别表示最大买进股票次数以及可以达到最大买进次数的方案数。
如果两种方案的买入日序列不同,但是价格序列相同,则认为这是相同的方案(只计算一次)。
数据范围
1≤N≤5000,
保证答案均不超过 int 范围。
输入样例1:
12
68 69 54 64 68 64 70 67 78 62
98 87
输出样例1:
4 2
输入样例2:
5
4 3 2 1 1
输出样例2:
4 1
题意: 给出n个数字,求其最长上升子序列长度以及不同的方案数。
分析: 求LIS过程比较简单,这道题复杂在统计不重复的方案数。首先可以开一个数组num,num[i]记录以a[i]结尾的LIS所有方案数,这个数组可以在dp数组更新时一起更新,这个过程有点类似dijkstra统计最短路条数。不过这样维护出来的num值显然是包含重复方案的,下一步考虑维护过程中去掉重复值。
第二重循环向前遍历j时,如果遇到和a[i]值相同的a[j],那num[j]应该置0,因为这里的num[j]一定会被包含在num[i]中,所以对于值相同的num值只需要记录最靠后的那个。另外也不需要担心这会影响之后的更新,对于j和i中间的数,这时num[j]还没清零,所以还能够正常更新,而i后面的数可以直接用更优的dp[i]来更新,同样不会影响结果。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
using namespace std;
int n, a[5005], dp[5005], num[5005];
signed main()
{
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
dp[i] = 1;
num[i] = 1;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j < i; j++){
if(a[j] > a[i]){
if(dp[i] < dp[j]+1){
dp[i] = dp[j]+1;
num[i] = num[j];
}
else if(dp[i] == dp[j]+1)
num[i] += num[j];
}
else if(a[j] == a[i])
num[j] = 0;
}
}
int len = 0, ans = 0;
for(int i = 1; i <= n; i++)
len = max(len, dp[i]);
for(int i = 1; i <= n; i++)
if(dp[i] == len)
ans += num[i];
cout << len << " " << ans << endl;
return 0;
}