(纪中)2224. 鸡国福利(kingdom)【最长不下降子序列】

(File IO): input:kingdom.in output:kingdom.out
时间限制: 3000 ms 空间限制: 262144 KB 具体限制
Goto ProblemSet


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


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

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


样例输入
Input1:
2 1
2 1

Input2:
3 2
1 2 3

Input3:
6 4
1 2 1 2 1 5

样例输出
Output1:
2

Output2:
3

Output3:
3


数据范围限制


提示
Sample1:
样例 1 1 1 中,可以让第 1 1 1 天来领钱的第 1 1 1 只鸡领 2 2 2 元( 1 1 1 袋),第 2 2 2 天来领钱的第 2 2 2 只鸡领 2 2 2 元( 2 2 2 袋),最多可以有 2 2 2 只鸡感到幸福。

Sample2:
样例 2 2 2 中,由于鸡国的平均收入为 2 2 2 元,所以领 1 1 1 元及以下的鸡是不会感到幸福。可以让第 1 1 1 天来领钱的第 1 1 1 只鸡领 2 2 2 元( 2 2 2 袋),第 2 2 2 天来领钱的第 2 2 2 只鸡领 2 2 2 元( 1 1 1 袋),第 3 3 3 天来领钱的第 3 3 3 只鸡领 3 3 3 元( 1 1 1 袋),最多可以有 3 3 3 只鸡感到幸福。

Sample3:
样例 3 3 3 中,由于鸡国的平均收入为 4 4 4 元,所以第 1 1 1 天,第 3 3 3 天,第 5 天来领钱的鸡不管领 1 1 1 袋钱,或者领 2 2 2 袋钱,或者不领都不会感到幸福。可以让第 2 2 2 天来领钱的第 2 2 2 只鸡领 4 4 4 元( 2 2 2袋),第 4 4 4 天来领钱的第 4 只鸡领 4 4 4 元( 2 2 2 袋),第 6 6 6 天来领钱的第 6 6 6 只鸡领 5 5 5 元( 1 1 1袋),最多可以有 3 3 3 只鸡感到幸福。


解题思路
正解是最长不下降子序列。

其实我们很容易就可以发现,答案要我们求出的那一串鸡所得到的“鸡币”数量是不下降的(如果是有下降的话,那么后面的鸡就没有前面的鸡那么幸福)
s o so so,我们就只用求出一个最长不下降子序列就好了。

我们可以设 f [ i ] f[i] f[i]为长度为 i i i的所有最长不下降子序列中,末尾(也就是子序列中的最大值)最小的那串的末尾。
然后这个 f f f 数组就不会出现 f [ i ] f[i] f[i] f [ j ] f[j] f[j]大的情况了( i < j i< j i<j),原因大家都懂吧!
如果 f [ i ] > f [ j ] f[i]>f[j] f[i]>f[j]的话,那么 f [ i ] f[i] f[i]不应该接在 f [ j ] f[j] f[j]的后面了么?

然后对于每一只新循环到的“鸡币”数量 a [ i ] a[i] a[i],我们都作以下处理:
首先,先看一下 a [ i ] a[i] a[i]是否比 m m m大,如果是的话就看看 a [ i ] a[i] a[i]是否大于等于 f [ l e n ] f[len] f[len]
如果 a [ i ] ≥ f [ l e n ] a[i]≥f[len] a[i]f[len],那该怎么办呢?那就说明 a [ i ] a[i] a[i]可以接在这个序列后面,同时最长的长度 l e n len len也要加 1 1 1
如果 a [ i ] a[i] a[i]无法接在序列后面,那就要考虑用它们改某一个f[j]的值了。
首先,我们要找到第一个大于 a [ i ] a[i] a[i] f [ j ] f[j] f[j]
找到了之后我们该怎么办呢?这时,我们会发现, f [ j − 1 ] f[j-1] f[j1]肯定是小于等于 a [ i ] a[i] a[i]的,而 f [ j ] f[j] f[j]又大于 a [ i ] a[i] a[i],那我们是不是可以把 a [ i ] a[i] a[i]接在 f [ j − 1 ] f[j-1] f[j1]的后面呢?这当然是可以的!那既然 a [ i ] < f [ j ] a[i]< f[j] a[i]<f[j]了,而把 a [ i ] a[i] a[i]接在 f [ j − 1 ] f[j-1] f[j1]后面形成的序列又是不下降的,我们就可以用 a [ i ] a[i] a[i]代替 f [ j ] f[j] f[j]了(因为a[i]比f[j]更小,也就更优了)
最后再考虑 a [ i ] ∗ 2 a[i]*2 a[i]2的情况就好了(打 i f if if语句时思路要清晰,不要弄错顺序了)!
下面我来讲一下一些让部分同学感到疑惑的问题吧!

问题一:如果 a [ i ] a[i] a[i]已经接在序列的末尾了,那么 a [ i ] ∗ 2 a[i]*2 a[i]2还要处理吗?
解答:这肯定是要处理的!不过 a [ i ] ∗ 2 a[i]*2 a[i]2绝对是不能接在队列的后面的(除非i等于 1 1 1

问题二:如果 a [ i ] a[i] a[i]已经修改了某一个f[j]的值了,那 a [ i ] ∗ 2 a[i]*2 a[i]2还可以改另一个f[j]的值吗?

比如说下面这种情况:
目前的 f f f 数组: 3 , 4 , 5 3 ,4 ,5 345
a [ i ] a[i] a[i] a [ i ] ∗ 2 : 12 a[i] *2:1 2 a[i]212

当我们用a[i]代替了 f [ 1 ] f[1] f[1]时,那到了处理 a [ i ] ∗ 2 a[i]*2 a[i]2时, f f f 数组就会变成这个样子: 145 1 4 5 145
再被 a [ i ] ∗ 2 a[i]*2 a[i]2处理一下, f f f数组不就会变成 125 1 2 5 125了吗?这样会不会不合法呢?

解答:这样做是对的,不用怕它不合法!

所以,大家都懂了吧!不懂请发私信。
后面我就附上标程了,思路正确可是却莫名其妙出错的同学可以看一下代码。


代码

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int n,m,k,a[1000010],f[1000010],len,t;
int hh(int x,int y)
{
    int l=1,r=y,mid=(l+r)/2,k=0;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(f[mid]>x)
        {
            k=mid;
            r=mid-1;
        }
        else
        l=mid+1;
    }
    return k;
}
int main(){
   freopen("kingdom.in","r",stdin);
   freopen("kingdom.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        if(a[i]>=m)
        {
            if(a[i]>=f[len])
                f[++len]=a[i];
            else
            {
                t=hh(a[i],len);
                f[t]=a[i];
            }
        }
         if(a[i]*2>=m)
        {
            if(a[i]*2>=f[len]&&(a[i]<f[len]||i==1))
                f[++len]=a[i]*2;
            else
            {
                t=hh(a[i]*2,len);
                f[t]=a[i]*2;
            }
        }
    }
    printf("%d",len);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值