[Daimayuan] 优美!最长上升子序列(C++,DP)

多组数据。

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

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

i 1 ∣ i 2 ∣ i 3 ∣ ⋯ ∣ i k i_1∣i_2∣i_3∣⋯∣i_k i1i2i3ik

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

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

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

输入格式

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

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

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

输出格式

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

数据规模

  • 1 ≤ T ≤ 100 1≤T≤100 1T100
  • 1 ≤ N ≤ 1 0 6 1≤N≤10^6 1N106,但保证 ∑ i = 1 T N i ≤ 1 0 6 \sum_{i=1}^{T}{N_i≤10^6} i=1TNi106
  • 1 ≤ A i ≤ 1 0 9 1≤A_i≤10^9 1Ai109

样例输入

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

解释:

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

对于第三组数组,选择 { A 1 , A 2 , A 4 , A 8 } = { 1 , 2 , 4 , 8 } \{A_1,A_2,A_4,A_8\}=\{1,2,4,8\} {A1,A2,A4,A8}={1,2,4,8}

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

解题思路

在寻找「优美」的最长上升子序列之前,我们先回顾一下最长上升子序列的寻找

for (int i = 1; i <= n; i++) {
	for (int j = 1; j < i; j++) {
		if (num[i] > num[j]) {
			pre[i] = max(pre[i], pre[j] + 1);
		}
	}
}

pre[i]中存储的是以对应num[i]结尾的最长上升子序列长度

要想寻找以num[i]结尾的最长上升子序列,我们只需要找到num[i]所能接上的最长前缀pre[j]( 1 ≤ j < i 1 \le j1j<i)即可

如果想采用同样的思路,我们会遇到一个问题

因为题目要求索引可以逐个整除,但是我们很难找到所有符合要求的索引

虽然向前找很难,但是向后找很容易,例如,对于 i i i,符合条件的索引即为 k ∗ i ( k ∈ Z ) k*i(k \in Z) ki(kZ)

所以我们更新一下算法

for (int i = 1; i <= n; i++) {
	for (int j = 2 * i; j <= n; j += i) {
		if (num[i] < num[j]) {
			pre[j] = max(pre[j], pre[i] + 1);
		}
	}
}

与常规动态规划的更新方式不同,我们不是根据过去的结果推导出当前的值,而是根据当前的结果去推导未来的值

这种方法可行的原理是:在到达pre[i]之前,我们已经尝试用所有符合条件的pre[j]( 1 ≤ j < i 1 \le j < i 1j<i)去更新pre[i]了,从结果来看,这与之前是一致的

然后计算一下时间复杂度 O ( N l o g 2 N ) O(Nlog_2N) O(Nlog2N),可以接受

最后,AC代码如下

#include 
using namespace std;
const int max_t = 100;
const int max_n = 1e6;
const int max_a = 1e9;

int t, n, arr[max_n + 1];
int dp[max_n + 1];

int main() {
	cin >> t;
	for (int i = 0; i < t; i++) {
		cin >> n;
		for (int j = 1; j <= n; j++) {
			cin >> arr[j];
		}

		int ans = 1;
		for (int j = 1; j <= n; j++) dp[j] = 1;
		for (int j = 1; j <= n; j++) {
			int z;
			for (z = 2 * j; z <= n; z += j) {
				if (arr[z] > arr[j]) {
					dp[z] = max(dp[z], dp[j] + 1);
					ans = max(ans, dp[z]);
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}

你可能感兴趣的:(动态规划,c++,动态规划)