2086 逢低吸纳 USACO (最长下降子序列+高精)

题目描述 Description

逢低吸纳”是炒股的一条成功秘诀。如果你想成为一个成功的投资者,就要遵守这条秘诀:

“逢低吸纳,越低越买”

这句话的意思是:每次你购买股票时的股价一定要比你上次购买时的股价低.按照这个规则购买股票的次数越多越好,看看你最多能按这个规则买几次。

给定连续的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

输入描述 Input Description

第1行: N (1 <= N <= 5000), 表示能买股票的天数。

第2行以下: N个正整数 (可能分多行) ,第i个正整数表示第i天的股价. 这些正整数大小不会超过longint(pascal)/long(c++).

输出描述 Output Description

只有一行,输出两个整数:

能够买进股票的天数和长度达到这个值的股票购买方案数量

在计算方案的数量的时候,如果两个方案的股价序列相同,那么这样的两个方案被认为是相同的(只能算做一个方案)。因此,两个不同的天数序列可能产生同一个股价序列,这样只能计算一次。

样例输入 Sample Input

12
68 69 54 64 68 64 70 67 78 62 98 87

样例输出 Sample Output

4 2

数据范围及提示 Data Size & Hint

见描述

思路

1丶 最长下降子序列模板
2丶统计方案数,由于方案数很大要用高精,还要判重

开一个二维数组sum[i][]储存在a[i]这个数时的方案数,
判重用一个bool数组
对于 sum[i]+sum[j] (a[i] < a[j]&&!b[a[j]]&&f[i]==f[j]+1)
只要标记了a[j] sum[i]就不会+sum[j]
这样先处理所有方案数,最后找f[i]==ans的方案数加起来就是总方案数

具体细节见代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 5010
using namespace std;
int a[MAXN],n,sum[MAXN][110],ansf[MAXN];
int f[MAXN],ans=1;
bool b[MAXN*10];
inline void read(int&x) {
    int f=1;x=0;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=10*x+c-48,c=getchar();
    x*=f;
}
inline void add(int *x,int *y) {
    int len=1,c[110]={0},t=0;
    while(len<=x[0]||len<=y[0]) {
        c[len]=x[len]+y[len]+t;
        t=c[len]/10;
        c[len]%=10;
        len++;
    }
    c[len]=t;
    while(!c[len]) len--;
    x[0]=len;
    for(int i=1;i<=len;i++) x[i]=c[i];
}
int main() {
    read(n);
    for(int i=1;i<=n;i++) read(a[i]),f[i]=1;
    for(int i=2;i<=n;i++)
      for(int j=1;j<i;j++) {
        if(a[j]>a[i]) {
            f[i]=max(f[i],f[j]+1);
            ans=max(ans,f[i]);
          }
      }//处理最长下降子序列 
    for(int i=1;i<=n;i++) {
        if(f[i]==1) {
            sum[i][0]=1;
            sum[i][1]=1;
        } //数组初始化 如果为1 说明这是一个 头 
        //就是说在a[i]之前没有比这个数更大的数
        //那么从f[i]开始方案数为1
        else {
            memset(b,false,sizeof b);
            sum[i][0]=1;
            for(int j=i-1;j>=1;j--) {
                if(a[i]<a[j]&&!b[a[j]]&&f[i]==f[j]+1) {
                    add(sum[i],sum[j]);
                    b[a[j]]=true;
                }// 因为a[i]<a[j] 那么sum[i]=sum[i]+sum[j]
            }
        }
    }
    memset(b,false,sizeof b);
    for(int i=n;i>=1;i--) {
        if(f[i]==ans&&!b[a[i]]) {//找最长的下降子序列,方案数相加
            add(ansf,sum[i]);
            b[a[i]]=true;
        }
    }
    printf("%d ",ans);
    for(int i=ansf[0];i>=1;i--) printf("%d",ansf[i]);
    return 0;
}
以下是P4087 [USACO17DEC]Milk Measurement的c++代码: ```c++ #include<bits/stdc++.h> using namespace std; int n,d,i,x,minn=1e9,maxn=-1e9,sum=7;//注意sum要初始化为7,因为一开始有三个人挤奶! map<int,int> mp; struct node{ int day,milk,id;//day表示某一天,milk表示这一天的产奶量,id表示这头牛的编号 }a[100010]; bool cmp(node x,node y){ return x.day<y.day; } int main(){ scanf("%d%d",&n,&d); for(i=1;i<=n;i++){ scanf("%d%d%d",&a[i].day,&a[i].id,&a[i].milk); minn=min(minn,a[i].id);//记录最小的牛的编号 maxn=max(maxn,a[i].id);//记录最大的牛的编号 } sort(a+1,a+n+1,cmp);//排序 for(i=1;i<=n;i++){ int p=a[i].id; mp[p]+=a[i].milk;//记录每头牛产奶总量 if(mp[p]-a[i].milk>=mp[minn]&&mp[p]>=mp[minn]){//如果这头牛的产奶总量减去这一天的产奶量后等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 sum--; } if(mp[p]>=mp[maxn]&&mp[p]-a[i].milk<mp[maxn]){//如果这头牛的产奶总量大于等于最大产奶量且这头牛的产奶总量减去这一天的产奶量小于最大产奶量 sum++; } if(mp[p]-a[i].milk<mp[maxn]&&mp[p]>=mp[maxn]){//如果这头牛的产奶总量减去这一天的产奶量小于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]-mp[p]+a[i].milk>0)sum++; } mp[p]-=a[i].milk;//减去这一天的产奶量 if(i==n||a[i].day!=a[i+1].day){//如果到了新的一天或者到了最后一天 if(mp[maxn]!=mp[a[i].id]&&mp[a[i].id]>=mp[maxn])sum++;//如果这头牛的产奶总量不等于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]==mp[a[i].id]){//如果这头牛的产奶总量等于最大产奶量 if(a[i].id==maxn)sum+=0;//如果这头牛就是最大产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } if(mp[minn]!=mp[a[i].id]&&mp[a[i].id]>=mp[minn])sum++;//如果这头牛的产奶总量不等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 if(mp[minn]==mp[a[i].id]){ if(a[i].id==minn)sum+=0;//如果这头牛就是最小产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } } } printf("%d\n",sum); return 0; } ``` 该题的解题思路是模拟,需要注意细节问题。我们可以首先将输入的数据按天数排序,然后模拟每一天挤奶的情况,并根据题目要求进行计数即可。具体细节请见代码注释。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值