zoukankan      html  css  js  c++  java
  • [HNOI2009]双递增序列

    不难发现本题贪心是不好做的,可以考虑 \(dp\)

    首先的一个想法就是令 \(dp_{i, j, k, l}\) 表示当前选到第 \(i\) 个位置,当前第一个序列选了 \(j\) 个数,当前第一个序列最后一个元素为 \(k\),第二个序列最后一个元素为 \(l\) 是否合法。这样的话转移十分显然,但复杂度过高了。进一步我们可以发现 \(k, l\) 中一定有一个是 \(a_i\),于是我们可以令 \(dp_{i, j, k, 0 / 1}\) 表示当前选到第 \(i\) 个位置,当前第一个序列选了 \(j\) 个数,选择 \(a_i\) 为结尾的另一个序列的结尾为 \(k\)\(i\) 是第一个序列还是第二个序列选择是否合法。这样的转移也很显然,不再赘述。

    下面这个操作就非常骚了,我们可以发现我们 \(dp\) 的值都是 \(0 / 1\),这样感觉非常的亏,我们能不能将状态中的某一维设计到 \(dp\) 值中去呢?答案是可以的,我们令 \(dp_{i, j, 0 / 1}\) 表示当前选到第 \(i\) 个位置,第一个序列选了 \(j\) 个数,\(a_i\) 是第一个还是第二个序列选时另一个序列结尾元素的最小值。那么我们就有转移:

    \(dp_{i, j, 0} = \min\{dp_{i, j, 0}, dp_{i - 1, j - 1, 0}\}(a_i > a_{i - 1})\)

    \(dp_{i, j, 0} = \min\{dp_{i, j, 0}, a_{i - 1}\}(a_i > dp_{i - 1, j - 1, 1})\)

    \(dp_{i, j, 1} = \min\{dp_{i, j, 1}, dp_{i - 1, j, 1}\}(a_i > a_{i - 1})\)

    \(dp_{i, j, 1} = \min\{dp_{i, j, 1}, a_{i - 1}\}(a_i > dp_{i - 1, j, 0})\)

    最终只需判断 \(dp_{n, \frac{n}{2}, 0 / 1} \ne \infty\) 即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 2000 + 5
    #define inf 1000000000
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    int T, n, a[N], dp[N][N][2];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int main(){
        T = read();
        while(T--){
            n = read();
            rep(i, 1, n) a[i] = read();
            rep(i, 1, n) rep(j, 0, min(i, n / 2)) dp[i][j][0] = dp[i][j][1] = inf;
            dp[1][1][0] = dp[1][0][1] = -1;
            rep(i, 2, n) rep(j, 0, min(i, n / 2)){
                if(a[i] > a[i - 1] && j >= 1) dp[i][j][0] = dp[i - 1][j - 1][0];
                if(j >= 1 && a[i] > dp[i - 1][j - 1][1]) dp[i][j][0] = min(dp[i][j][0], a[i - 1]);
                if(a[i] > a[i - 1]) dp[i][j][1] = dp[i - 1][j][1];
                if(a[i] > dp[i - 1][j][0]) dp[i][j][1] = min(dp[i][j][1], a[i - 1]);
            }
            if(dp[n][n / 2][0] != inf || dp[n][n / 2][1] != inf) puts("Yes!");
            else puts("No!");
        }
        return 0;
    }
    
  • 相关阅读:
    洛谷 P6295
    洛谷 P4240
    洛谷 P3287
    My rating is 1064.
    洛谷 P5071
    C语言 #include <> 与 #include “” 区别
    C语言 #pragma once
    C语言 typedef 和 define 区别
    C语言 define 定义函数(多行书写)
    C语言 define 定义函数
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13434684.html
Copyright © 2011-2022 走看看