📜  门| GATE CS 2021 |设置1 |问题27(1)

📅  最后修改于: 2023-12-03 15:12:37.984000             🧑  作者: Mango

门 | GATE CS 2021 | 设置1 | 问题27

本文是为准备参加 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;
}
总结

这是一道比较典型的动态规划问题,需要对状态转移方程进行细致的分析,才能写出代码实现。

同时,在进行状态转移的过程中,需要对数组长度和序列长度的关系,以及前一个差异值和当前差异值的大小比较进行判断,以保证状态转移的正确性。