【问题标题】:Minimum of maximums for k-size nonconsecutive subsequence of array数组的 k 大小非连续子序列的最大值中的最小值
【发布时间】:2022-12-20 06:26:39
【问题描述】:

假设我有一个数组,arr = [2, 3, 5, 9] 和 k = 2。我应该找到长度为 k 的子序列,使得每个子序列中没有两个元素相邻。然后找到这些序列的最大值。最后,找到最大值中的最小值。例如,对于 arr,有效子序列为 [2,5]、[3,9]、[2,9],最大值分别为 5、9 和 9。预期输出将是最大值中的最小值,即 5。

除了蛮力之外,我想不出任何其他方法来解决这个问题,即用嵌套的 for 循环找到所有可能的子序列,然后找到最大值和最小值。根据要求,有更好的方法,但我不知道那是什么。贪婪的? DP?

【问题讨论】:

  • @bloomsdayforever 示例中的数组按升序排序。但我想一般来说它根本不需要排序,对吧?
  • #1:质疑规范,它是缺乏的。究竟什么是相邻的?相邻的数字,例如 1,2,3... 或数组中的相邻数字?究竟是什么构成了一个子序列,它必须是不断增加的数字,还是任何顺序不相邻的任何 k 个数字都可以吗?到底什么是“最大值”、子序列中所有项目的总和、子序列中存在的最大数,或者什么?两个两个子序列 [3,9] 和 [2,9] 如何比较?假设读者“在字里行间”发现这些东西的规范写得不好。
  • 如果有多个最大值,使用什么打破平局的逻辑?

标签: arrays algorithm dynamic-programming greedy


【解决方案1】:

我想了一段时间如何解决这个问题。最终我想出了一个解决方案。但是,我不确定,所以我决定将我的解决方案作为一个单独的问题发布。这是:Minimum of maximums of non-consecutive subsequences of size k。

我决定等待一些时间来获得答案或 cmet,它们可以确保我的解决方案是正确的,或者可以给我一些改进建议。现在已经过去一周了,我个人看不出有什么改进的方法,所以我把它发布在这里。

我想这个解决方案可能不是最有效的解决方案。至少我希望它是正确的,至少要尽我所能去验证它的正确性。

解决这个问题我想出了这个问题中没有说明的两个假设。我希望他们能让问题更容易解决。他们是:

  1. 输入序列的元素是唯一的。

  2. 对于输入子序列S和数字k、2 <= k <= (length(S) + 1) / 2。我发现这个假设本身是必要的,因为它拒绝了子序列根本无法确定的情况,因此不可能是它们的最大值中的最小值。

    我打算尝试消除第一个假设。但如果这会使算法更难证明其正确性或实施起来,我就不会这样做。

    伪代码,版本 1

    find_k_length_sequence_maxes_min (S, k)
        if k < 2 or length(S) < 2 * k - 1
            return NO_SUCH_MINIMUM
    
        sorted = copy(S)
        sort_ascending(sorted)
    
        for t from k - 1 to length(S)
            current_length = 0
    
            index = find_index(S, sorted[t])
    
            last_index = index
    
            for u descending from index to 1
                if u < last_index - 1 && S[u] <= sorted[t]
                    current_length += 1
                    last_index = u
    
                if current_length >= k
                    return sorted[t]
    
            last_index = index
    
            for u ascending from index to length(S)
                if u > last_index + 1 and S[u] <= sorted[t]
                    current_length += 1
                    last_index = u
    
                if current_length >= k
                    return sorted[t]
    

    伪代码,版本 2

    (这与版本 1 中的算法相同,只是使用更自然的语言编写。)

    (1) 令S 是一个整数序列,它的所有元素都是唯一的。

    (2) 令“S 的非连续子序列”表示S 的任何两个元素在S 中不相邻的子序列。

    (3) 令k 是满足2 &lt;= k &lt;= (length(S) + 1) / 2 的整数。

    (4) 找出长度为k的S的所有非连续子序列的最大值中的最小值。

    (4.1) 找到S 的最小元素,使其成为大小为k 的S 的非连续子序列的最大值。

    (4.1.1) 设sorted 为S 的排列,其元素按升序排列。

    (4.1.2) 对于sorted的每个元素e,检查它是否是长度为k的S的非连续子序列的最大值。如果是,请将其退回。

    (4.1.2.1) 设x和y为整数,使得1 &lt;= x &lt;= index(minmax(k))和index(minmax(k)) &lt;= y &lt;= length(S)。

    (4.1.2.2) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得e是它们中每一个的最大值。

    (4.1.2.3)检查all(1, index(e))最长序列的长度是否大于等于k。如果是,返回e。

    (4.1.2.4)检查all(1, index(e))的最长子序列长度和all(index(e), length(S))的最长子序列长度之和是否大于等于k。如果是,返回e。

    正确性证明

    我不记得我是否曾经为一个程序写过任何正确性证明,所以我很确定这个程序的质量是否可以改进。对它持保留态度。如果可以,我鼓励你自己检查算法是否正确。

    (1) 词汇表:

    • “观察”是指不是从任何观察或结论中得出的陈述,不需要证明,

    • “结论”是指从至少一个观察或结论得出的陈述,不需要证明,

    • 我所说的“定理”是指不是从任何观察或结论中得出的陈述,需要证明。

    (2) 令S 是一个整数序列,使得它的所有元素都是唯一的。

    (3) 令“S 的非连续子序列”表示S 的任何两个元素在S 中不相邻的子序列。

    (4) 令k 是满足2 &lt;= k &lt;= (length(S) + 1) / 2 的整数。

    (5) 令minmax(k) 是S 的一个元素,使得它是S 的所有长度为k 的非连续子序列的最大值中的最小值。

    (6) (定理)minmax(k) 是S 的最小元素,使得它是长度为k 的S 的非连续子序列的最大值。

    (7) 换句话说,S中没有元素小于minmax(k),这是S的长度为k的非连续子序列的最大值。

    (8)((6)的证明)(观察)由于minmax(k)是S长度为k的所有非连续序列的最大值中的最小值,因此不存在S的非连续子序列长度为k,使其最大值大于minmax(k)。

    (9)((6)的证明)(结论)如果(6),则S小于minmax(k)的任何元素不能是S长度为k的任何非连续子序列的最大值。

    (10) ((6) 的证明) QED

    (11) 设x和y是满足1 &lt;= x &lt;= index(minmax(k))和index(minmax(k)) &lt;= y &lt;= length(S)的整数。

    (12) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得minmax(k)是它们中每一个的最大值。

    (13)(观察)minmax(k)是all(1, length(S))的最长序列中的最大值。

    (14) 这个观察结果可能看起来太微不足道了。但是,显然,考虑到最长的子序列而不是长度为 k 的子序列,我更容易编写和证明算法。因此,我认为这一观察值得注意。

    (15) (定理)可以通过以下方式产生最长的all(1, index(minmax(k)))序列:

    • 从minmax(k)开始,

    • 搬到S[1],

    • 始终取下一个小于或等于minmax(k) 且不与上一个取用的元素相邻的元素。

    (16) ((15)的证明)让S 的“可能元素”表示小于或等于minmax(k) 并且与最后一个元素不相邻的元素。

    (16a) ((15)的证明)设C为(15)中产生的子序列。

    (17)(证明(15))(观察)

    • 在第一个被取元素之前,恰好有 0 个可能的元素,

    • 在任意两个被取元素(不包括它们)之间,恰好有 0 或 1 个可能的元素,

    • 在最后一个元素之后,恰好有 0 或 1 个可能的元素。

    (18) ((15)的证明)令D 是all(1, index(minmax(k))) 的序列,使得length(D) &gt; length(C)。

    (19)((15)的证明)至少满足以下条件之一:

    • 在第一个被取元素之前,D 中可能的元素少于 0 个,

    • 在两个被取元素(不包括它们)之间使得它们之间在C中有 1 个可能的元素,在D 中有 0 个可能的元素,

    • 在最后一个元素之后,D 中可能的元素少于 1 个。

    (20)(证明(15))(观察)

    • 在第一个被取元素之前不能有少于 0 个可能的元素,

    • 如果D中的两个被取元素(不包括它们)之间可能的元素少于1,而C中有1,这意味着我们取了大于minmax(k)的元素,或者与minmax(k)相邻的元素最后采取的一个,与(12)相矛盾,

    • 如果D 中最后一个元素之间的可能元素少于 1 个,而在 C 中有 1 个元素,则意味着我们已经获取了大于 minmax(k) 的元素,或者与最后一个元素相邻的元素一,与(12)相矛盾。

    (21) ((15) 的证明) QED

    (22)(观察)(15) 也适用于all(index(minmax(k)), length(S))。

    (23)(观察)length(all(1, length(S))) = length(all(1, index(minmax(k)))) + length(all(index(minmax(k)), length(S)))。

    执行

    如果任何 assert 调用都没有中止程序,则所有测试都会通过。

    #include <limits.h> // For INT_MAX
    #include <assert.h> // For assert
    #include <string.h> // For memcpy
    #include <stdlib.h> // For qsort
    
    int compar (const void * first, const void * second) {
        if (* (int *)first < * (int *)second) return -1;
        else if (* (int *)first == * (int *)second) return 0;
        else return 1;
    }
    
    void find_k_size_sequence_maxes_min (int array_length, int array[], int k, int * result_min) {
        if (k < 2 || array_length < 2 * k - 1) return;
    
        int sorted[array_length];
        memcpy(sorted, array, sizeof (int) * array_length);
        qsort(sorted, array_length, sizeof (int), compar);
    
        for (int t = k - 1; t < array_length; ++t) {
            int index = -1;
            while (array[++index] != sorted[t]);
    
            int size = 1;
    
            int last_index = index;
            for (int u = index; u >= 0; --u) {
                if (u < last_index - 1 && array[u] <= sorted[t]) {
                    ++size;
                    last_index = u;
                }
    
                if (size >= k) {
                    * result_min = sorted[t];
                    return;
                }
            }
    
            last_index = index;
            for (int u = index; u < array_length; ++u) {
                if (u > last_index + 1 && array[u] <= sorted[t]) {
                    ++size;
                    last_index = u;
                }
    
                if (size >= k) {
                    * result_min = sorted[t];
                    return;
                }
            }
        }
    }
    
    int main (void) {
        // Test case 1
        int array1[] = { 6, 3, 5, 8, 1, 0, 9, 7, 4, 2, };
        int array1_length = (int)((double)sizeof array1 / sizeof (int));
        int k = 2;
        int min = INT_MAX;
        find_k_size_sequence_maxes_min(array1_length, array1, k, & min);
        assert(min == 2);
    
        // Test case 2
        int array2[] = { 1, 7, 2, 3, 9, 11, 8, 14, };
        int array2_length = (int)((double)sizeof array2 / sizeof (int));
        k = 2;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array2_length, array2, k, & min);
        assert(min == 2);
    
        // Test case 3
        k = 3;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array2_length, array2, k, & min);
        assert(min == 8);
    
        // Test case 4
        k = 4;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array2_length, array2, k, & min);
        assert(min == 9);
    
        // Test case 5
        int array3[] = { 3, 5, 4, 0, 8, 2, };
        int array3_length = (int)((double)sizeof array3 / sizeof (int));
        k = 3;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array3_length, array3, k, & min);
        assert(min == 3);
    
        // Test case 6
        int array4[] = { 18, 21, 20, 6 };
        int array4_length = (int)((double)sizeof array4 / sizeof (int));
        k = 2;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array4_length, array4, k, & min);
        assert(min == 18);
    
        // Test case 7
        int array5_length = 1000000;
        int array5[array5_length];
        for (int m = array5_length - 1; m >= 0; --m) array5[m] = m;
        k = 100;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array5_length, array5, k, & min);
        assert(min == 198);
    }
    

    如果您有任何问题或提示,或者看到任何错误或错误,请发表评论。


    编辑:正如我所写的,我试图消除第一个假设。我觉得我成功了,就是这个假设可以去掉。

    只需要很少的改动。值得注意的是,现在我使用不定冠词“a”出现的所有术语“最小”和“最大”。我想表达的是,S 中可能有不止一个元素具有最小值,S 中可能有不止一个元素具有最大值。

    伪代码,没有元素唯一性的版本 1

    线

    index = find_index(S, sorted[t])
    

    应替换为该行

    index = find_first_index(S, sorted[t])
    

    伪代码,没有元素唯一性的版本 2

    (这与版本 1 中的算法相同,只是使用更自然的语言编写。)

    (1) 设S为整数序列。

    (2) 令“S 的非连续子序列”表示S 的任何两个元素在S 中不相邻的子序列。

    (3) 令k 是满足2 &lt;= k &lt;= (length(S) + 1) / 2 的整数。

    (4) 找出长度为k的S的所有非连续子序列的最大值中的最小值。

    (4.1) 找到S 的最小元素,使得它是大小为k 的S 的非连续子序列的最大值。

    (4.1.1) 设sorted 为S 的排列,其元素按升序排列。

    (4.1.2) 对于sorted的每个元素e,检查它是否是长度为k的S的非连续子序列的最大值。如果是,请将其退回。

    (4.1.2.1) 设x和y为整数,使得1 &lt;= x &lt;= index(minmax(k))和index(minmax(k)) &lt;= y &lt;= length(S)。

    (4.1.2.2) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得e是其中每一个的最大值。

    (4.1.2.3)检查all(1, index(e))最长序列的长度是否大于等于k。如果是,返回e。

    (4.1.2.4)检查all(1, index(e))的最长子序列长度和all(index(e), length(S))的最长子序列长度之和是否大于等于k。如果是,返回e。

    没有元素唯一性的证明

    第 (2) 点现在应该是:

    (2) 设S为整数序列。

    第 (5) 点现在应该是:

    (5) 令minmax(k) 是S 的一个元素,使得它是S 的所有非连续子序列中长度为k 的最大值中的最小值。

    第 (8) 点现在应该是:

    (8)((6)的证明)(观察)由于minmax(k)是长度为k的S的所有非连续序列的最大值中的最小值,因此不存在S的非连续子序列长度为k,使其最大值大于minmax(k)。

    第 (12) 点现在应该是:

    (12) 设all(x, y)为S在S[x](包括)和S[y](包括)之间的所有非连续子序列的集合,使得minmax(k)是其中每一个的最大值。

    没有元素唯一性的实现

    应该添加以下测试用例:

        // Test case 8 (no uniqueness)
        int array6[] = { 18, 21, 21, 6 };
        int array6_length = (int)((double)sizeof array6 / sizeof (int));
        k = 2;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array6_length, array6, k, & min);
        assert(min == 18);
    
        // Test case 9 (no uniqueness)
        int array7[] = { 18, 21, 18, 6 };
        int array7_length = (int)((double)sizeof array7 / sizeof (int));
        k = 2;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array7_length, array7, k, & min);
        assert(min == 18);
    
        // Test case 9 (no uniqueness)
        int array8[] = { 18, 18, 20, 6 };
        int array8_length = (int)((double)sizeof array8 / sizeof (int));
        k = 2;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array8_length, array8, k, & min);
        assert(min == 18);
    
        // Test case 10 (no uniqueness)
        int array9[] = { 18, 18, 21, 6 };
        int array9_length = (int)((double)sizeof array9 / sizeof (int));
        k = 2;
        min = INT_MAX;
        find_k_size_sequence_maxes_min(array9_length, array9, k, & min);
        assert(min == 18);
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    • 2021-04-11
    • 2016-06-13
    • 2017-01-10
    • 2021-06-12
    • 1970-01-01
    • 2021-12-09
    相关资源
    最近更新 更多