代码源:699、并行排序

287 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情logo.png

题目描述

这是4月2日代码源div1的每日一题。

知识点:二分+动态规划

并行排序 - 题目 - Daimayuan Online Judge

给定一个 n ,以及长度为 n 的排列 a ,如果两个点 i,j 满足i<j 并且 a[i]>a[j] ,那么这两个点之间存在一条边,现在请你对这 n 个点染色,要求任意一条边的两点颜色不同,且使用的颜色数量最少,输出最少使用的颜色数量。

输入格式

第一行一行数字 T ,代表 T 组数据。

每组数据第一行一个数字 n。

接下来一行 n 个整数 a1,a2,…,an。

输出格式

一个数,表示最少需要的颜色数量。

样例1输入

2
4
1 3 4 2
2
1 2

样例1输出

2
1

数据规模

所有数据保证 1≤∑n≤10^6。

问题解析

首先我们知道,只有满足i<j 并且 a[i]>a[j] 的两个点中间有一条边,而且这条边两边的两个点颜色不能一样。

然后我们知道,如果是正常的升序情况,即i<j且a[i]<=a[j]的两个点是没有边的,说明他们颜色可以一样。

那么这两个情况我们就可以知道了,序列里,只要是升序的序列用的颜色都可以一样,反之降序的必须不一样,比如4 3 2 1,1的颜色要和前面三个数的不一样,2要和前面两个不一样,3要和前面一个不一样,因为4有一种颜色,所以一共需要4种颜色才能满足要求。这样题目就变成了,最长下降子序列的长度是多少,因为只用看下降序列是什么样的,下降序列多长就要用多少种不同的颜色,升序的只用算一种就可以了。只不过这里数据长度是10^6,用普通的求最长下降子序列的方法是n^2,显然是会超时的,所以要用上二分+dp的方法。至于二分+dp怎么求最长下降子序列可以去2022-02-12每日刷题打卡你好Ä的博客-CSDN博客看看,这里便不多做描述。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 300050;
int a[N], f[N];

inline int read() {
    int x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}

void write(int x) {
    if (x > 9) write(x / 10);
    putchar(x % 10 | '0');
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        vector<int>v(n), f(n+1);
        for (int i = 0; i < n; i++)
        {
            cin >> v[i];
        }
        int len = 1;
        f[1] = v[0];
        for (int i = 1; i < n; i++)
        {
            int l = 0, r = len;
            while (l < r)
            {
                int mid = (l + r+1) / 2;
                if (f[mid] > v[i])l = mid;
                else r = mid-1;
            }
            len = max(len, r+1);
            f[r + 1] = v[i];
        }
        cout << len << endl;
    }
    
    return 0;
}