题解 | #合唱队#

合唱队

https://www.nowcoder.com/practice/6d9d69e3898f45169a441632b325c7b4

状态转移方程:

考虑每人的添加与否,下个人与当前或者上个人进行比较。因此,当前人是否考虑,会影响到 有效队列末尾的身高、有效队列人数这两个要素。

因此,f[i][j]可以为前i个人形成的有效队列中,队尾身高为j的总人数;也可以为前i个人形成的有效队列中,总人数为j时队尾的身高。

代码尝试如下:

(1)

考虑顶峰来自于n个数中,因此遍历n个数,将其作为顶峰;

对每个顶峰dp,f[j][k]表示前i个数队列,末尾元素j的次数。

分在顶峰左侧、顶峰、顶峰右侧进行dp.

<15%通过率>

(2)

看题解,自己的思路比较复杂了。

问题:方法_1进行了n次二维dp,且当前dp的结果可能被下次dp使用

因此,对递增、递减进行二次二维dp。

<15%通过率>

dp数组初始值0 和 当前值0影响。将该种情况嵌入后

<20%通过率>

数值上限设置维1000,修改为hei_max+1之后

<45%通过率> - 内存超限

(3)

针对方法_进行优化。

使用一个数组,处理递增、递减的二次二维dp。

<65%通过率>

将j由队列的末尾值修改为元素个数,降低dp数组范围

<95%通过率>

f[i]由f[i-1]决定(不考虑j),使用滚动数组。

<100%通过率>

/******************************************************************************

                              Online C++ Compiler.
               Code, Compile, Run and Debug C++ program online.
Write your code in this editor and press "Run" button to compile and execute it.

*******************************************************************************/

#include <iostream>
#include <vector>

using namespace std;

/***
<sample input>
8
186 186 150 200 160 130 197 200
<sample output>
4
*/

int main() {
    int n;
    cin >> n;
    vector<int> hei(n + 1);
    int hei_max = 0;
    for (int i = 1; i <= n; i++) {
        cin >> hei[i];
        if (hei[i] > hei_max) hei_max = hei[i];
    }

    vector<int> up_rec(n+1, 0);
    // vector<vector<int>> up(n + 2, vector<int>(n + 2,
    //                        0x7f7f));  // n+2因为递减数组,存在n+1的计数
    vector<int> up(n+2, 0x7f7f7f7f);      // up[i]仅与up[i-1]有关,因此使用滚动数组。

    up[0] = 0;   // 这里的j时有效队列的最后一个数,与当前值进行比较
    // f[i][?] == hei[i],?表示从开头->cur的最大递增长度;
    for (int i = 1; i <= n; i++) {
        vector<int> pre = up;   // 保存前一个状态
        for (int j = 0; j <= n; j++) {
            // 找到上一个阶段的有效值
            if (pre[j] <= hei_max) {
                // 严格递增,或者当前元素为0、队末元素未初始化
                if ((hei[i] == 0 && pre[j] == 0) || hei[i] > pre[j]) {
                    // 取该元素
                    up[j + 1] = min(up[j + 1], hei[i]); // 保存递增顺序的最小值
                    // 不取该元素
                } 
                // 不满足,该元素不可取
                up[j] = min(up[j], pre[j]);
                // printf("i:%d, j:%d, pre[j]:%d, up[j]:%d, up[j+1]:%d\n",
                //         i, j, pre[j], up[j], up[j+1]);
            }
        }

        // dp时候保存之前的结果
        for (int j = 1; j <= n; j++) {
            if (up[j] == hei[i]) {
                up_rec[i] = j;
                // printf("i:%d, j:%d\n", i, j);
                break;
            }
        }
    }

    // // f[i][hei[i]],此时cur->n的最大递减长度;
    vector<int>& down = up;
    for (int i = 0; i < n + 2; i++) {
        down[i] = 0x7f7f7f7f;
    }

    down[0] = 0;    // down[i]记录前i个数字有效队列的末尾元素,从后向前遍历
    for (int i = n; i >= 1; i--) {
        vector<int> pre = down;     // 记录前一个状态
        // j代表有效递增序列的起始元素
        for (int j = 0; j <= n; j++) {
            // 找到上一个状态(i+1)的有效队列末尾元素 j
            if (pre[j] <= hei_max) {
                // 严格递减
                if ((hei[i] == 0 && pre[j] == 0) || hei[i] > pre[j]) {
                    // 取该元素
                    down[j + 1] = min(down[j + 1], hei[i]);
                }

                // 不满足,该元素不可取
                down[j] = min(down[j], pre[j]);
            }
        }
        for (int j = 1; j <= n; j++) {
            if (down[j] == hei[i]) {
                // down_rec[i-1].push_back(j);
                up_rec[i] += j;
                // printf("i:%d, down_len:%d\n", i, j);
                break;
            }
        }
    }

    // // // 综合递增、递减长度
    int max_len = 0;
    for (int i = 1; i <= n; i++) {
        // int tmp = up_rec[i] + down_rec[i] - 1;
        if (up_rec[i]-1 > max_len) max_len = up_rec[i]-1;
        // printf("i:%d, up[i]:%d, down[i]:%d\n", i, up[i][hei[i]], down[i][hei[i]]);
    }

    cout << n - max_len;
    return 0;
}
全部评论

相关推荐

点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务