dp2.牛牛的数列

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

牛牛现在有一个n个数组成的数列,牛牛现在想取一个连续的子序列,并且这个子序列还必须得满足:最多只改变一个数,就可以使得这个连续的子序列是一个严格上升的子序列,牛牛想知道这个连续子序列最长的长度是多少。

输入描述:

输入包括两行,第一行包括一个整数n(1 ≤ n ≤ 10^5),即数列的长度;
第二行n个整数a_i, 表示数列中的每个数(1 ≤ a_i ≤ 10^9),以空格分割。

输出描述:

输出一个整数,表示最长的长度。

示例1

输入

6

7 2 3 1 5 6

输出

5

示例2

输入

6

1 2 4 2 5 6

输出

4


定义[start1, end1]为第一个严格递增子序列,[start2, end2]为第二个严格递增子序列,start2 = end1 + 1;

当start1 == end1, start2 == end2时,说明严格递增子序列只有1个数;

两个严格递增子序列可以合并的情况:

1. start1 == end1,第一个严格递增子序列只有一个数,可以变化该数,使得这个数小于第二个严格递增子序列的最小的数,也就是nums[start2];

2. start2 == end2,第二个严格递增子序列只有一个数,可以变化该数,使得这个数大于第一个严格递增子序列的最大的数,也就是nums[end1];

3. 当start1 != end1时,说明第一个严格递增子序列至少有2个数,当start2 != end2时,说明第二个严格递增子序列至少有2个数;

    当第二个严格递增子序列的第二个数 > 第一个严格递增子序列最大的数 + 1,即nums[start2 + 1] > nums[end1] + 1,可以把nums[start2]变化,

    使得这两个子序列可以合并;

    当第一个严格递增子序列的倒数第二个数 < 第二个严格递增子序列最小的数 - 1,即nums[end1 - 1] < nums[start1] - 1,可以把nums[end1]变化,

    使得这两个子序列可以合并;

两个严格递增子序列不能合并时:

此时的答案是这两个子序列长度最长的长度 + 1,1是较短的子序列的拿出最大的数或者最小的数拼接到长的序列上。

迭代更新[start1, end1]和[start2, end2],找到答案最大的值。

#include <iostream>
#include <vector>

using namespace std;

int solution(vector<int> &nums) {
    int n = nums.size();
    if (n <= 0)
        return 0;
    int res = 0;
    int start1 = 0, end1 = start1;
    while (end1 < n - 1 && nums[end1] < nums[end1 + 1])
            end1++;
    // end1 == n - 1代表整个数组严格递增
    if (end1 == n - 1)
        return end1 - start1 + 1;
    // end1 < n - 1 保证[start2, end2]至少有数字
    while (end1 < n - 1) {
        int start2 = end1 + 1;
        int end2 = start2;
        while (end2 < n - 1 && nums[end2] < nums[end2 + 1])
            end2++;
        if (start1 == end1 || start2 == end2 || nums[start2 + 1] - nums[end1] > 1 || nums[start2] - nums[end1 - 1] > 1)
            res = max(res, end2 - start2 + end1 - start1 + 2);
        else 
            res = max(max(end2 - start2, end1 - start1) + 2, res);
        start1 = start2;
        end1 = end2;
    }
    return res;
}

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    cout << solution(nums) << endl;
    return 0;
}

来源于我看到的一篇题解,感觉方法很好,讲解的也很仔细,题解人:

<think> 题目要求修改代码,使得输入在遇到换行符(即用户输入回车)时结束循环,而不是在输入0时结束。原代码是通过检测输入0来结束循环的。 但是,C++中标准输入`cin`在读取整数时,默认会跳过空白字符(包括空格、制表符和换行符)。因此,直接使用`cin >> x`在遇到换行符时并不会退出循环,因为换行符会被跳过,直到遇到非空白字符。如果用户直接输入回车,`cin >> x`会等待下一个非空白字符。 为了实现“输入换行符表示结束”,我们需要改变输入方式。我们可以使用`getline`读取一行,然后解析该行中的整数。如果读取到空行(即用户直接输入回车),则退出循环。 修改思路: 1. 使用`string line;`和`getline(cin, line);`来读取一行。 2. 如果读取的行为空(长度为0),则退出循环。 3. 否则,使用字符串流(`istringstream`)解析该行中的所有整数,并添加到向量`h`中。 4. 注意:原代码是每次读取一个整数,现在变成每次读取一行,一行中可能有多个整数(以空格分隔)。但题目描述并没有明确说明输入格式,原代码是每行输入一个整数(因为`cin>>x`会跳过空白),现在修改为可以在一行中输入多个整数(以空格分隔),同时也可以多行输入,直到遇到空行(即直接回车)结束输入。 不过,原输入方式是在输入0时结束,且0不计入序列。现在要求用空行结束,所以我们需要在遇到空行时退出循环。 修改步骤: - 包含头文件:`#include <sstream>` - 使用`getline`读取一行 - 判断该行是否为空,如果为空则退出循环 - 不为空则用字符串流解析该行中的所有整数 注意:题目要求输入换行符(回车)结束。所以我们在输入过程中,可以在输入完最后一个数据后,在新的一行直接按回车表示结束。 但是,原代码中还有一点:原代码在输入0时退出且0不计入序列。修改后,我们不再以0为结束标志,而是以空行为结束标志。因此,输入0不会被特殊处理,0会被当作正常数据。 然而,题目要求修改为“输入换行符enter表示数据输入结束”,所以0现在也是有效数据。 因此,我们需要调整代码,去掉对0的判断。 修改后的输入循环: 另外,原代码中输出两个答案:`ans1`和`ans2`,然后使用`system("pause")`暂停。我们保留这部分,但注意`system("pause")`在非Windows系统可能不可用,但题目没有说明系统,我们假设环境允许。 修改代码: 但是,注意原代码中第一个问题求解的是最长不上升子序列(非递增),第二个问题是最长上升子序列(用于计算最少系统数,即Dilworth定理:最小链划分等于最长反链)。 我们只需要修改输入部分。 以下是修改后的代码: </think>要修改代码,使得输入在遇到换行符(Enter)时结束循环,可以使用`getline`读取整行输入,然后解析该行中的整数。以下是修改后的代码: ```cpp #include <iostream> #include <vector> #include <algorithm> #include <sstream> // 添加字符串流支持 #include <string> // 添加字符串支持 using namespace std; int main() { vector<int> h; string line; // 读取整行输入 while (getline(cin, line)) { // 如果输入空行(直接按Enter),则退出循环 if (line.empty()) break; // 使用字符串流解析行中的整数 stringstream ss(line); int x; while (ss >> x) { h.push_back(x); } } int n = h.size(); if (n == 0) { cout << "0\n0" << endl; return 0; } // 问题1:最长不上升子序列 vector<int> dp1; for (int x : h) { auto it = upper_bound(dp1.begin(), dp1.end(), x, greater<int>()); if (it == dp1.end()) { dp1.push_back(x); } else { *it = x; } } int ans1 = dp1.size(); // 问题2:最长上升子序列(最少系统数) vector<int> dp2; for (int x : h) { auto it = lower_bound(dp2.begin(), dp2.end(), x); if (it == dp2.end()) { dp2.push_back(x); } else { *it = x; } } int ans2 = dp2.size(); cout << ans1 << "\n" << ans2 << endl; return 0; } ``` ### 修改说明: 1. **输入方式变更**: - 使用 `getline(cin, line)` 读取整行输入 - 若 `line` 为空(即直接按Enter),则退出循环 - 使用 `stringstream` 解析行中的整数 2. **移除0退出逻辑**: - 删除了 `if (x == 0) break`,现在只依赖空行退出 - 所有输入的数字(包括0)都会被处理 3. **输入格式示例**: ```plaintext 300 250 275 200 180 190 (直接按Enter结束) ``` ### 关键代码解释: ```cpp string line; while (getline(cin, line)) { if (line.empty()) break; // 检测到空行退出 stringstream ss(line); // 创建字符串流 int x; while (ss >> x) { // 从流中提取整数 h.push_back(x); } } ``` - `getline(cin, line)`:读取包括空格的一整行,遇到Enter结束 - `line.empty()`:检测是否为空行(用户直接按Enter) - `stringstream`:将字符串转换为流,支持`>>`操作符提取整数 - `ss >> x`:从流中提取整数直到行尾 ### 测试示例: **输入**: ``` 300 250 275 200 180 190 ``` **输出**: ``` 2 3 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值