鸡国福利 (dp)

题目链接:http://exam.upc.edu.cn/problem.php?id=3637

题目描述

鸡国为了表彰鸡国每一只鸡在过去一年的优秀表现,打算在接下来的 n 天中每天给鸡国的一只鸡发 1 袋或者 2 袋“鸡币”(鸡国的通用货币)作为福利。国王要求每天来领钱鸡
互不相同,即来领过钱的鸡不能再来,否则将受到严厉的处罚。 
但聪明的鸡国老百姓侦察后发现国王每天发的钱袋子里面装的钱数量是不一样的(同一天的相同),第 i 天发的每一袋钱为 a i 元。如果第 i 天来领钱的鸡领 1 袋钱,它可以获得ai 元的“鸡币”,如果它领 2 袋钱,则可以获得 2×ai 元“鸡币”,当然它也可以放弃,则第i 天的钱国王收回国库。 
由于鸡国生活条件优越和鸡的贪念等原因,当第 i 天领钱的鸡同时满足以下两个条件时它才会感到幸福: 
(1)领到的钱不能低于鸡国的平均收入 m 元。 
(2)要跟它前面领了钱且感到幸福的鸡一样幸福或者更幸福。 
仁慈的国王希望鸡国的每一只鸡都能感到幸福,请你帮国王规划一下在这 n 天中怎样给每一只发钱才能让最多的鸡感到幸福? 

输入

输入共 2 行。 
第 1 行输入两个整数 n 和 m,分别表示发钱的天数(或理解为来领钱的鸡数)和鸡国的平均收入。 
第 2 行 n 个正整数 ai  (1≤i≤n),依次表示第 i 天发的一袋钱中的“鸡币”为 ai 元。。 

输出

输出 1 行一个整数,表示最多可以让多少只鸡感到幸福。 

样例输入

2 1
2 1

样例输出

2

思路:就是求最长不减子序列的长度。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
const int maxn = 2e5 + 5;
int a[maxn],b[maxn],c[maxn],d[maxn],n,m,cnt,ans;
//a[]表示每天钱袋中的钱数 
int lowbit(int k){
    return k & -k;
}
 
void update(int k, int num){
    while(k <= cnt){
        d[k] = max(d[k],num);
        k += lowbit(k);
    }
}
 
int query(int k){
    int ret = 0;
    while(k){
        ret = max(ret,d[k]);
        k -= lowbit(k);
    }
    return ret;
}
 
int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i = 1; i <= n; i ++)
            scanf("%d",&a[i]);
        cnt = 0;
        b[++ cnt] = m;//存储平均收入 
        for(int i = 1; i <= n; i ++){//b[]存储可能的领取情况,领*1 或者 *2 
            b[++ cnt] = a[i];   b[++ cnt] = 2 * a[i];
        }
        sort(b+1,b+cnt+1);//排序,便于进行二分查找 
        cnt = unique(b+1,b+cnt+1) - (b + 1);//去掉重复元素,把重复元素放到数组尾端,返回非重复下标 
        m = lower_bound(b+1,b+cnt+1,m) - b; //返回第一个大于等于m的下标 
        for(int i = 1; i <= n; i ++){
            c[i] = lower_bound(b+1,b+cnt+1,2*a[i]) - b;//c[i]中存放第i天领*2的下标 
            a[i] = lower_bound(b+1,b+cnt+1,a[i]) - b;
        } 
        memset(d,0,sizeof(d));
        ans = 0;
        for(int i = 1; i <= n; i ++){
            int ok;//因为b[]按照升序排列,所欲下标大的元素值就大 
            if(c[i] >= m){//第i天领*2的情况且>=m  第i只鸡领的钱数 
                ok = query(c[i]);
                ans = max(ans,ok+1);
                update(c[i],ok+1);
            } 
            if(a[i] >= m){//第i天领*1的情况且>=m 
                ok = query(a[i]);  //查询在此之前有多少零钱的鸡 
                ans = max(ans,ok+1);
                update(a[i],ok+1);
            }
            //两个if语句不能够颠倒,如果先查询更新a[i]的话,则在查询c[i]的时候求的前缀值可能是
			//a[i]中得出的, 
        }
        printf("%d\n",ans);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值