“逢低吸纳”是炒股的一条成功秘诀。如果你想成为一个成功的投资者,就要遵守这条秘诀:
"逢低吸纳,越低越买"这句话的意思是:每次你购买股票时的股价一定要比你上次购买时的股价低.按照这个规则购买股票的次数越多越好,看看你最多能按这个规则买几次。给定连续的N天中每天的股价。你可以在任何一天购买一次股票,但是购买时的股价一定要比你上次购买时的股价低。写一个程序,求出最多能买几次股票。
以下面这个表为例, 某几天的股价是:
天数 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
PROGRAM NAME: buylow
INPUT FORMAT:
(file buylow.in)
第1行: N (1 <= N <= 5000), 表示能买股票的天数。
第2行以下: N个正整数 (可能分多行) ,第i个正整数表示第i天的股价. 这些正整数大小不会超过longint(pascal)/long(c++).
OUTPUT FORMAT:
(file buylow.out)
只有一行,输出两个整数:
能够买进股票的天数 长度达到这个值的股票购买方案数量
在计算解的数量的时候,如果两个解所组成的字符串相同,那么这样的两个解被认为是相同的(只能算做一个解)。因此,两个不同的购买方案可能产生同一个字符串,这样只能计算一次。
input:
12
68 69 54 64 68 64 70 67
78 62 98 87
output:
4 2
这道题两个问,第一个就是求最长下降子序列,第二个问就是求其方案数。
第一个问十分好求,可以用O(n^2)DP算法来求,f[i]=max(f[j]+1)(1<=j<i;a[j]>a[i])
也可以用O(nlongn)的二分算法来求。这里重点讲一下求第二个问,求其方案数。
很容易想到的是要求a[i]为结尾的最长下降子序列,要满足的条件是(1<=j<i,a[j]>a[i],f[i]=f[j]+1),
只有满足上面的条件以a[i]为结尾的最长下降子序列方案数才能加上a[j]的方案数。
所以tot[i]=sum(tot[j](1<=j<i,a[j]>a[i],f[i]=f[j]+1))。但是还有一种情况就是2 1 1,
这样用上面的状态方程就会重复计算,本来的方案数是1,而这样计算就变成了2了。所以要判重。
而判重的方法网上有很多种,我这样讲其中的两种,一个是leokan牛的用bool来判重,还有一种就是
usaco上的判重方法。
leokan牛的方法是将出现过的数用bool标记,这样出现过的数就不会被用到了,这样条件就变成了
(1<=j<i,a[j]>a[i],f[i]=f[j]+1,visit[a[j]]=false)
这样就可以判断重复的数,求出总的方案数。
下面来说一下usaco上的判重方法。
now=-1; //用一个now判断数是否的重复
for(int j=i-1;j>=0;--j)
if(f[j]+1==f[i]&&a[j]>a[i]&&now!=a[j]){
add(tot[i],tot[j]);
now=a[j];
}
计算总数的时候要避免产生重复计算,即 2 1 1 这种情况,下面代码 l 的作用,当数据出现
2 1 1 的这种情况,l 可以很容易的判断重复,但是如果数据是 6 5 4 3 2 3 2 1,数据中有
两个2,如何才能正确的判断重复呢?其实很容易的,我们可以知道如果要满足以1为结尾的最
长下降子序列数一定 >1 而且满足f[i]=f[j]+1,那么当l的值等于2时那么它就不会在问3的时候改变,
因为3的f[i]=f[j]+2的,所以在问第一个2的时候l还是等于2,这样就把两个2给判重了,
下面的代码就是利用了这一点当前统计方案的这个数与前面的可以加入的那些数的条件
就是(f[i]=f[j]+1 且 now!=a[j] (1<=j<i));这里a[j]一定要逆推上去。
O(n^2)+bool判重
#include<cstdio>
#include<cstring>
#define max(a,b)(a>b?a:b)
using namespace std;
long n,a[5001],f[5001],tot[5001][100],ans[100],maxn=0;
bool visit[20000];
void input(){
freopen("buylow.in","r",stdin);
scanf("%d",&n);
for(long i=1;i<=n;i++){
scanf("%d",&a[i]);
f[i]=1;
}
}
void add(long *a,long *b,long *c){
long cc[100];
memset(cc,0,sizeof(cc));
cc[0]=max(a[0],b[0]);
for(long i=1;i<=cc[0];i++)
cc[i]=a[i]+b[i];
for(long i=1;i<=cc[0];i++){
cc[i+1]+=cc[i]/10;
cc[i]%=10;
}
if(cc[cc[0]+1]>0) cc[0]++;
memcpy(c,cc,sizeof(long)*100);
}
void solve(){
for(long i=1;i<=n;i++){
for(long j=1;j<=i-1;j++)
if(a[i]<a[j] && f[i]<f[j]+1) f[i]=f[j]+1;
maxn=max(maxn,f[i]);
}
for(long i=1;i<=n;i++)
if(f[i]==1){
memset(tot[i],0,sizeof(tot[i]));
tot[i][0]=1;
tot[i][1]=1;
}
else{
memset(tot[i],0,sizeof(tot[i]));
tot[i][0]=1;
memset(visit,1,sizeof(visit));
for(long j=i-1;j>=1;j--)
if(f[i]==f[j]+1 && a[i]<a[j] && visit[a[j]]){
add(tot[i],tot[j],tot[i]);
visit[a[j]]=0;
}
}
memset(visit,1,sizeof(visit));
for(long i=n;i>=1;i--)
if(f[i]==maxn && visit[a[i]]){
add(ans,tot[i],ans);
visit[a[i]]=0;
}
}
void output(){
freopen("buylow.out","w",stdout);
printf("%d ",maxn);
for(long i=ans[0];i>1;i--) printf("%d",ans[i]);
printf("%d\n",ans[1]);
}
int main()
{
input();
solve();
output();
return 0;
}
速度:
Executing...
Test 1: TEST OK [0.022 secs, 4732 KB]
Test 2: TEST OK [0.000 secs, 4732 KB]
Test 3: TEST OK [0.000 secs, 4732 KB]
Test 4: TEST OK [0.011 secs, 4732 KB]
Test 5: TEST OK [0.011 secs, 4728 KB]
Test 6: TEST OK [0.022 secs, 4728 KB]
Test 7: TEST OK [0.043 secs, 4732 KB]
Test 8: TEST OK [0.022 secs, 4732 KB]
Test 9: TEST OK [0.043 secs, 4732 KB]
Test 10: TEST OK [0.227 secs, 4728 KB]
O(nlongn):
#include<iostream>
using namespace std;
int n,a[5000],f[5000],c[5000],size;
int tot[5000][30],ans[30];
int bsearch(int *ci,int ai){
int l=1,r=size-1;
while(l<=r){
int mid=(l+r)>>1;
if(ai<ci[mid-1]&&ai>=c[mid]) return mid;
else if(ai<ci[mid]) l=mid+1;
else r=mid-1;
}
}
void add(int *ai,int *bi){
if(bi[0]>ai[0]) ai[0]=bi[0];
for(int i=1;i<=ai[0];++i) ai[i]+=bi[i];
for(int i=1;i<=ai[0];++i){
ai[i+1]+=ai[i]/10000;
ai[i]%=10000;
}
if(ai[ai[0]+1]) ai[0]++;
}
int main()
{
freopen("buylow.in","r",stdin);
freopen("buylow.out","w",stdout);
scanf("%d",&n);
for(int i=0;i<n;++i) scanf("%d",&a[i]);
c[0]=a[0]; f[0]=size=1;
int now;
for(int i=1;i<n;++i){
if(a[i]>=c[0]) now=0;
else if(a[i]<c[size-1]) now=size++;
else now=bsearch(c,a[i]);
c[now]=a[i]; f[i]=now+1;
}
printf("%d ",size);
for(int i=0;i<n;++i){
memset(tot[i],0,sizeof(tot[i]));
if(f[i]==1)
tot[i][0]=tot[i][1]=1;
else{
tot[i][0]=1;
now=-1;
for(int j=i-1;j>=0;--j)
if(f[j]+1==f[i]&&a[j]>a[i]&&now!=a[j]){
add(tot[i],tot[j]);
now=a[j];
}
}
}
now=-1;
memset(ans,0,sizeof(ans));
ans[0]=1;
for(int i=n-1;i>=0;--i)
if(f[i]==size&&now!=a[i]){
add(ans,tot[i]);
now=a[i];
}
printf("%d",ans[ans[0]]);
for(int i=ans[0]-1;i>=1;--i) printf("%04d",ans[i]);
printf("\n");
return 0;
}
速度:
Executing...
Test 1: TEST OK [0.000 secs, 3356 KB]
Test 2: TEST OK [0.000 secs, 3356 KB]
Test 3: TEST OK [0.000 secs, 3360 KB]
Test 4: TEST OK [0.000 secs, 3360 KB]
Test 5: TEST OK [0.000 secs, 3360 KB]
Test 6: TEST OK [0.000 secs, 3356 KB]
Test 7: TEST OK [0.000 secs, 3360 KB]
Test 8: TEST OK [0.011 secs, 3360 KB]
Test 9: TEST OK [0.011 secs, 3360 KB]
Test 10: TEST OK [0.043 secs, 3360 KB]
<coolcode lang="cpp" download="HelloWorld.cpp">
for(int i=1;i<=n;++i)
</coolcode>