饥饿的牛(最长递增子序列)

链接:https://ac.nowcoder.com/acm/problem/25148
来源:牛客网

题号:NC25148
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目:

题目描述

Farmer John养了N(1 <= N <= 5,000)头奶牛,每头牛都有一个不超过32位二进制数的正整数编号。FJ希望奶牛们在进食前,能按编号从小到大的顺序排好队,但奶牛们从不听他的话。为了让奶牛们养成这个习惯,每次开饭时,FJ从奶牛中顺序地挑出一些,这些奶牛的编号必须按挑出的顺序递增。然后FJ让被挑出的奶牛们吃饭——其他奶牛就只能饿肚子了。
现在,你得到了这一次开饭前队伍中从前到后所有奶牛的编号。奶牛们想请你计算一下,按照FJ的规定,最多有多少头奶牛能吃上饭?
比如说,有11头奶牛按以下顺序排好了队(数字代表奶牛的编号)
2 5 18 3 4 7 10 9 11 8 15
对于这个队列,最多可以让7头奶牛吃上饭,她们的编号分别为2,3,4,7,10,11,15。队列2,5,3,10,15是不合法的,因为第3头奶牛的编号(3)小于她前面一头奶牛的编号(5)。

输入描述:

第1行: 一个整数,N
第2..?行: 除了最后一行,每一行都包含恰好20个用空格隔开的整数,依次表 示队伍中从前到后的奶牛的编号。如果N不能整除20,那么最后一行包含的数字不到20个

输出描述:

第1行: 输出按照FJ的规定,最多可以挑出的奶牛的数目
示例1

输入

11
2 5 18 3 4 7 10 9 11 8 15

输出

7

 

方法一:O(logn),很难懂

i 0 1 2 3 4 5 6
arr[i] 1 7 3 4 9 4

8

 

j 0 ......
number[j] -INT_MAX  

 

开始判断:

刚开始时候,p指向number数组的j位置(p=0),arr[0]=1>number[1]      =>     p++,number[p]=arr[0]

j 0 1 ......
number[j] -INT_MAX 1(arr[0])   

此时p=1,arr[1]=7>number[p]      =>    p++,number[p]=arr[1]

j 0 1 2 ......
number[j] -INT_MAX 1(arr[0])  7(arr[1)  

此时p=2,arr[2]=3<number[p]      =>    搜索如果将3插入到number数组中,应该在的位置,用lower_bounder函数,发现position=2,则number[position]=arr[2]=3

j 0 1 2 ......
number[j] -INT_MAX 1(arr[0])  7(arr[1)->3(arr[2])  

 此时p=2,arr[3]=4>number[p]     =>  p++,number[p]=arr[3]

j 0 1 2 3 ......
number[j] -INT_MAX 1(arr[0])  3(arr[2]) 4(arr[3])  

 此时p=3,arr[4]=9>number[p]     => p++,number[p]=arr[4] 

j 0 1 2 3 4 ......
number[j] -INT_MAX 1(arr[0])  3(arr[2]) 4(arr[3]) 9(arr[4])  

 此时p=4,arr[5]=4<number[p]    =>  position=lower_bounder(number,number+p,arr[i])=3,number[position]=arr[5]=4

j 0 1 2 3 4 ......
number[j] -INT_MAX 1(arr[0])  3(arr[2]) 4(arr[3])->4(arr[5]) 9(arr[4])  

 此时p=4,arr[6]=8<number[p]    =>  position=lower_bounder(number,number+p,arr[i])=4,number[position]=arr[6]=8

j 0 1 2 3 4 ......
number[j] -INT_MAX 1(arr[0])  3(arr[2]) 4(arr[3]/arr[5]) 9(arr[4])->8(arr[6])  

 

number数组的结果:

j 0 1 2 3 4
number[j] -INT_MAX 1(arr[0])  3(arr[2]) 4(arr[3]/arr[5]) 8(arr[6])

number下标最高位4表示最长的就是长度为4的子序列 

复制代码
/*
-------------------------------------------------
   Author:       wry
   date:         2022/3/1 17:10
   Description:  test
-------------------------------------------------
*/

#include <bits/stdc++.h>

using namespace std;

const int MAXN =5000+10;
int arr[MAXN];
int number[MAXN];    //表示以number[i]结尾的最长子序列有i个

int main() {
    int n;
    cin >> n;
    for (int i=0;i<n;i++) {
        cin >> arr[i];
    }
    number[0] = -INT_MAX;    //0号位置作为哨兵
    int p = 0;    //定位需要处理的number位置
    for (int i=0;i<n;i++) {
        if (arr[i]>number[p]) {
            p++;
            number[p] = arr[i];
        }
        else if(arr[i]<=number[p]) {
            int position = lower_bound(number,number+p,arr[i]) - number;    //返回arr[i]可以在number数组中插入的位置
            number[position] = arr[i];    //将值变成最小的
        }
    }
    cout << p << endl;

    return 0;
}
复制代码

 

 

方法二:递归+记忆化 O(n^2)

Note:用maxlength数组记录下标为j结尾的最长子序列长度,初始化为-1表示还没有处理过,但是当处理它时,将它初始化为1,表示最少就有长度为1的最长子序列,每次对比它之前的所有值,如果比它小就判断它自身已知的最长子序列和以比它小的那个子序列长度+1,取其中更大的值

复制代码
/*
-------------------------------------------------
   Author:       wry
   date:         2022/3/1 17:10
   Description:  test
-------------------------------------------------
*/

#include <iostream>

using namespace std;

const int MAXN =5000+10;
int arr[MAXN];
int maxlength[MAXN];    //记录每个元素的最长子序列长度

int Fun(int j) {    //表示以下标为j元素结尾,最长子序列
    if (maxlength[j]!=-1) {
        return maxlength[j];
    }
    if (j==0) {
        maxlength[j] = 1;
    }
    else {
        maxlength[j] = 1;    //因为每个值至少都是1开始
        for (int i=0;i<j;i++) {
            if (arr[i]<arr[j]) {
                maxlength[j] = max(maxlength[j],Fun(i)+1);
            }
        }
    }
    return maxlength[j];
}

int main() {
    int n;
    cin >> n;
    fill(maxlength,maxlength+n,-1);   //初始化
    for (int i=0;i<n;i++){
        cin >> arr[i];
    }
    int maxlong = -1;
    for (int i=0;i<n;i++) {
        maxlong = max(maxlong,Fun(i));
    }
    cout << maxlong << endl;
    return 0;
}
复制代码

 

(这是我第二次写的,能通过本题,也能通过北大POJ最长递增子序列

复制代码
/*
-------------------------------------------------
   Author:       wry
   date:         2022/3/4 0:45
   Description:  test
-------------------------------------------------
*/

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN = 5000+10;
int arr[MAXN];
int length[MAXN];     //以i结尾的最长递增子序列

void Fun(int n) {
    for (int i=0;i<n;i++) {
        length[i] = 1;
        if (i!=0) {
            for (int j = 0; j < i; j++) {
                if (arr[j] < arr[i] && length[j] + 1 > length[i]) {
                    length[i] = length[j] + 1;
                }
            }
        }
    }
}

int main() {
    int n;
    cin >> n;
    for (int i=0;i<n;i++) {
        cin >> arr[i];
    }
    Fun(n);
    int maxl = length[0];
    for (int i=1;i<n;i++) {
        maxl = max(maxl,length[i]);
    }
    cout << maxl << endl;
    return 0;
}
复制代码

 

posted @   火星架构师  阅读(191)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示