BUY LOW, BUY LOWER
Time Limit: 1000MS |
|
Memory Limit: 30000K |
Total Submissions: 4652 |
|
Accepted: 1563 |
Description
The advice to "buy low" is half the formula to success in the bovine stock market.To be considered a great investor you must also follow this problems' advice:
"Buy low; buy lower"
Each time you buy a stock, you must purchase it at a lower price than the previous time you bought it. The more times you buy at a lower price than before, the better! Your goal is to see how many times you can continue purchasing at ever lower prices.
You will be given the daily selling prices of a stock (positive 16-bit integers) over a period of time. You can choose to buy stock on any of the days. Each time you choose to buy, the price must be strictly lower than the previous time you bought stock. Write a program which identifies which days you should buy stock in order to maximize the number of times you buy.
Here is a list of stock prices:
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
The best investor (by this problem, anyway) can buy at most four times if each purchase is lower then the previous purchase. One four day sequence (there might be others) of acceptable buys is:
Day 2 5 6 10
Price 69 68 64 62
Input
* Line 1: N (1 <= N <= 5000), the number of days for which stock prices are given
* Lines 2..etc: A series of N space-separated integers, ten per line except the final line which might have fewer integers.
Output
Two integers on a single line:
* The length of the longest sequence of decreasing prices
* The number of sequences that have this length (guaranteed to fit in 31 bits)
In counting the number of solutions, two potential solutions are considered the same (and would only count as one solution) if they repeat the same string of decreasing prices, that is, if they "look the same" when the successive prices are compared. Thus, two different sequence of "buy" days could produce the same string of decreasing prices and be counted as only a single solution.
Sample Input
12
68 69 54 64 68 64 70 67 78 62
98 87
Sample Output
4 2
Source
USACO 2002 February
经典的LIS问题,关键是求不重复的子串数量
DP时多求了一个为了记录前面不重复的子串数量
#include<cstring> #include<algorithm> #define N 5005 using namespace std; int a[N],d[N],g[N],n; int lis() { int i,j,ans=0; memset(d,0,sizeof(d)); memset(g,1,sizeof(g)); for(i=1;i<=n+1;i++) { j=lower_bound(g+1,g+n+1,a[i])-g; d[i]=j; g[j]=a[i]; if(i!=n+1) ans=max(ans,d[i]); } return ans; } int main() { int i,j,x,ans; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",a+i); a[i]=-a[i]; } a[0]=-(1<<30); ans=lis(); g[0]=1; for(i=1;i<=n+1;i++) { g[i]=0; x=1; for(j=i-1;j>=0;j--) if(a[i]>a[j]) { if(d[j]+1==d[i]&&a[j]!=x) { g[i]+=g[j]; x=a[j]; } else if(d[j]+1>d[i]) { g[i]=g[j]; x=a[j]; } } } printf("%d %d/n",ans,g[n+1]); }
http://blog.csdn.net/yc0576/archive/2010/04/14/5486640.aspx
O(nlogn)算法
设原来的序列为A[],不失一般性,我们只考虑严格递增的情况(原题是递减)
用L[i]表示以A[i]结尾的LIS的长度
用cnt[i]纪录以A[i]结尾的且长度为L[i]的本质不同的LIS的数量,首先用nlogn的算法计算出所有元素的L值
然后依次扫描A[1],A[2],……A[n],每扫一个元素,就统计该元素的cnt的值,然后把元素插入到一个list链表中
list[k]只保存L值为k的元素,list[k]中的元素按升序排列(相同元素只保留一个),一共有n个list。
例如对于序列,1,5,3,2,6
list[1]:1
list[2]:2,3,5
list[3]: 6
此时如果扫描到一个4,那么显然该元素的L值为3。
也就是说4应该插入list[3]中。在插入之前必须先统计以元素4的cnt值。
计算这个cnt值的方法如下:在list[2]中找到第一个比4小的元素i(例子中就是3),那么从表头元素到i的所有元素的cnt总和就是4的cnt值。
关键在于如何快速找到list[2]中第一个比4小的元素,我们只要纪录一个pos指针,初始化的时候pos指向链表最末尾元素,查找的过程相当于向左移动指针的过程,直到遇到一个比4小的元素。如果下次仍然是在list[2]中查找,那么只要从pos开始往前查找即可(想想为什么不用从尾元素开始)。
得到以4结尾的cnt值后,再把4插入到list[3]中,由于4一定是最小的元素(想想为什么),所以一定是头插入。
最后要注意的是如果list[k]有两个元素相同,只留下一个。
注:L[i]需要在扫描前就计算出来
#include <iostream> #include <algorithm> #include <vector> using namespace std; const int INF = 99999999; const int N = 5050; int last[N]; int a[N]; int f[N]; int n; vector<int> list[N]; int p[N]; int sum_cnt[N]; int cnt[N]; int b_search(int left, int right, int k) { while (left <= right) { int mid = (left+right)/2; if (last[mid] > a[k]) left = mid + 1; else right = mid - 1; } return left; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); memset(last, 0, sizeof(last)); a[0] = last[0] = INF; f[0] = 1; for (int i = 1; i <= n; i++) { int idx = b_search(0, i-1, i); last[idx] = a[i]; f[i] = idx; } list[0].push_back(0); sum_cnt[0] = 1; for (int i = 1; i <= n; i++) { int idx = f[i]-1; while (a[list[idx][p[idx]]] <= a[i]) { sum_cnt[idx] -= cnt[list[idx][p[idx]]]; p[idx]++; } cnt[i] = sum_cnt[idx]; if (list[idx+1].size() > 0 && a[list[idx+1].back()] == a[i]) { sum_cnt[idx+1] += cnt[i] - cnt[list[idx+1].back()]; list[idx+1].back() = i; } else { list[idx+1].push_back(i); sum_cnt[idx+1] += cnt[i]; } } int idx = 0; for (int i = 1; i <= n; i++) if (f[idx] < f[i]) idx = i; printf("%d %d/n", f[idx], sum_cnt[f[idx]]); }