📅  最后修改于: 2023-12-03 15:12:37.984000             🧑  作者: Mango
本文是为准备参加 GATE CS 2021 的程序员准备的介绍文章。
在一个字符数组中,每个字符都有一个整数值。定义一个“减法序列”如下:$a_1$ 和 $a_2$ 是序列的第一个元素,对于 $i > 2$,$a_i$ 是前面所有元素值之差的绝对值的最小非零值。例如,如果序列以 $1, 4, 7, 11$ 开始,则下一个元素必须是 $abs(11-7)$,因为 $1, 4, 7, 11$ 的差分为 $3, 3, 4$。我们想知道当输入一个长度为 n 的字符数组后,至多可以生成多少长度为 k 的非严格单调递增的减法序列。
请实现一个函数,输入参数为字符数组 a,数组长度 n,可生成的序列长度 k,函数的输出应为最多可以生成的减法序列的数量。
函数原型为:
long long max_sequences(char a[], int n, int k);
输入格式:
第一行包含一个整数 t,表示测试用例的数量。
每个测试用例包含两行。第一行包含两个整数 n 和 k。第二行包含 n 个整数,代表字符数组 a。
输出格式:
对于每个测试用例,输出一个整数代表最多可以生成的减法序列的数量。
第一个案例中,可以生成的减法序列有 (2, 2, 2), (3, 3), (3, 1, 3), (1, 3, 2, 2) 和 (2, 1, 2, 3) 等多种。其中最长的序列长度为 4,因此输出为 4。
这是一道动态规划问题。
我们定义一个二维的状态矩阵 $dp[i][j]$ 表示从字符数组的第 $i$ 个位置开始生成长度为 $j$ 的减法序列的最大数量。
对于每个位置 $i$,都可以考虑取或不取这个数,两种情况分别对应对状态矩阵进行更新。
状态转移方程如下:
$$dp[i][j]=\begin{cases}1&i=n\dp[i+1][j-1]&j=1\max(dp[i+1][j-1], dp[k][j-1]+1)&1<j \leq k \leq n, a[k]-a[i]\leq D\end{cases}$$
其中,$D$ 表示当前减法序列之前的差值。
根据上述状态转移方程可以写出对应的函数。
long long max_sequences(char a[], int n, int k) {
long long dp[n + 1][k + 1];
memset(dp, 0, sizeof(dp));
for (int i = n - 1; i >= 1; i--) {
for (int j = 1; j <= k; j++) {
dp[i][j] = 1;
if (j == 1) {
dp[i][j] = dp[i + 1][j - 1];
} else {
for (int p = i + 1; p <= n && p <= i+j-1; p++) {
if (abs(a[p] - a[i]) <= abs(a[i + 1] - a[i])) {
dp[i][j] = max(dp[i][j], dp[p][j - 1] + 1);
} else {
dp[i][j] = max(dp[i][j], dp[p][j - 1]);
}
}
}
}
}
long long ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, dp[i][k]);
}
return ans;
}
这是一道比较典型的动态规划问题,需要对状态转移方程进行细致的分析,才能写出代码实现。
同时,在进行状态转移的过程中,需要对数组长度和序列长度的关系,以及前一个差异值和当前差异值的大小比较进行判断,以保证状态转移的正确性。