Luogu P1410 子序列

Posted guessycb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P1410 子序列相关的知识,希望对你有一定的参考价值。

题目大意:

给定一个长度为\(N\)\(N\)为偶数)的序列,]
问能否将其划分为两个长度为\(\frac{N}{2}\)的严格递增子序列,
输入一共有\(50\)组数据,每组数据保证\(N \leq 2*10^3\)

题目解法:

非常巧妙的一道\(DP\)题。
$f[i][j] $ 表示到了第\(i\)个位置,
第一个序列中有\(j\)个元素,第二个序列中最后一个元素大小为\(f[i][j]\)
显然最小化\(f[i][j]\)是最优策略。
然后转移有两种:
( 1 )如果\(h[i]<h[i+1]\),那么\(i+1\)可以放入第一个序列:
\[f[i+1][j+1] = min(f[i+1][j+1]\ , \ f[i][j])\]
( 2 )如果\(f[i][j]<h[i+1]\),那么\(i+1\)可以放入第二个序列:
\[f[i+1][i+j-1] = min(f[i+1][i+j-1],h[i])\]
其实就是交换了第一个序列与第二个序列。

实现代码:

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define _ 2005
#define INF 2147483640
using namespace std;

IL int gi(){
    RG int data = 0 , m = 1; RG char ch = 0;
    while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if(ch == '-'){m = 0; ch = getchar();}
    while(ch >= '0' && ch <= '9')
    {data = (data << 1) + (data << 3) + (ch ^ 48); ch = getchar(); }
    return ( m ) ? data : -data;
}

int f[_][_] , h[_] , n;

int main(){
    while(scanf("%d",&n) != EOF){
        for(RG int i = 1; i <= n; i ++)h[i] = gi();
        for(RG int i = 1; i <= n; i ++)
            for(RG int j = 0; j <= i; j ++)
                f[i][j] = INF;
        f[1][1] = -INF;
        for(RG int i = 1; i <= n; i ++)
            for(RG int j = 1; j <= i; j ++)
                if(f[i][j] != INF){
                    if(h[i] < h[i+1])
                        f[i+1][j+1] = min(f[i][j] , f[i+1][j+1]);
                    if(f[i][j] < h[i+1])
                        f[i+1][i-j+1] = min(f[i+1][i-j+1] , h[i]);
                }
        puts( (f[n][n/2]!=INF) ? "Yes!" : "No!" );
    }return 0;
}

以上是关于Luogu P1410 子序列的主要内容,如果未能解决你的问题,请参考以下文章

P1410 子序列

洛谷P1410 子序列

P1410 子序列

P1410 子序列

洛谷 P1410 子序列(DP)

luogu P2757 [国家集训队]等差子序列