代码源:671、优美!最长上升子序列

308 阅读3分钟

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

题目描述

这是4月6日代码源div2的每日一题。

知识点:dp

优美!最长上升子序列 - 题目 - Daimayuan Online Judge

多组数据。

每组将给定一个数组。派派希望从中选择一个递增的子序列,越长越好。

但派派认为,这样选出来的子序列依然不够「优美」,形式化的讲,派派希望选择的下标(从 1 开始)需要满足

i1∣i2∣i3∣⋯∣ik

其中 a|b 表示整除, 即 a 是 b 的约数。

请你帮助派派完成任务吧!

注:子序列的含义不再赘述。

输入格式

第一行一个整数 T,表示接下来有 T 组数据。

每组数据包含两行,第一行包含一个整数 N。

随后一行,包含 N 个整数,表示原数组 {A}。

输出格式

对于每组数据,输出一行,包含一个数,表示能选出的「优美」的最长上升子序列长度。

数据规模

  • 1≤T≤100
  • 1≤N≤10^6,但保证 ∑i=1TNi≤10^6
  • 1≤Ai≤10^9

样例输入

4
4
1 4 6 7
2
2 2
10
1 2 3 4 5 6 7 8 9 10
10
10 9 8 6 5 2 3 1 2 1

样例输出

3
1
4
1

解释:

对于第一组数据,能选择的「优美」最长上升子序列为 {A1,A2,A4}={1,4,7}。

对于第三组数组,选择 {A1,A2,A4,A8}={1,2,4,8}。

对于第四组数据,可选择的「优美」最长上升子序列长度为 1。

问题解析

一开始有点懵,不理解是什么意思,后来发现跟一般的求最长上升子序列差不多。

这里和一般的求最长上升子序列就多了一个条件:选择的最长上升子序列,他们在数组v里的位置(从1开始,不是0)要呈倍数关系。那我从位置1开始,只看我枚举位置的倍数能不能凑成上升子序列就行。比如我枚举的位置是2,那我就看4 6 8……这些位置能不能和第二个位置凑成上升序列。我们可以准备一个dp数组,dp[i]的意思是,以v[i]为结尾的最长上升子序列的长度。我们就看,如果倍数位置j的数大于我们枚举的位置i的数,就看是当前dp[j]存的位置和dp[i]+1那个大。我们选最大的。最后输出dp数组里存储的最大值。

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>

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 4e5 + 50;

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;
    t=read();
    while (t--)
    {
        int n, mx = 1;
        n=read();
        vector<int>v(n+1), dp(n+1,1);
        for (int i = 1; i <= n; i++)v[i]=read();
        for (int i = 1; i <= n; i++)
        {
            for (int j = i * 2; j <= n; j += i)
            {
                if (v[j] > v[i])dp[j] = max(dp[j], dp[i] + 1);
                mx = max(mx, dp[j]);
            }
            
        }
        write(mx);
        putchar('\n');
    }
    return 0;
}