NKOJ P3500 独立集【动态规划】

很有意思的一道题了。

首先冒泡排序当且仅当两个数是逆序的时候才会移动,也就是有边相连,那么对于一个独立集,一定是满足两两之间不存在逆序关系,也就是独立集的数在原序列当中一定是一个上升的子序列。所以第一问就是让我们求序列的最长上升子序列的长度。

由于最大独立集不唯一,也就是可能会有多个最长上升子序列,那么如何才能判断当前数字一定在所有的最长上升子序列中呢?

如果一个点不是唯一的,那么这个点就可以被另外一个与它相同的点给替换掉。所以如果一个数的向左最长下降长度与向右最长上升长度独一无二,并且在满足第一问的情况下,那么这个点就是满足条件的。

另,一般求最长上升子序列与最长下降子序列的方法是 O ( n 2 ) ,但是这道题显然需要更优秀的写法,也就是 O ( n l o g 2 n )

#include <map>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define Make make_pair
#define Pair pair<int,int>
using namespace std;
const int Max=1e5+5;
const int Inf=1e9;
map<Pair,int>Map;
int N,Ans_Num,Ans[Max],DP1[Max],DP2[Max];
int LenA,A[Max],AA[Max];
int LenB,B[Max],BB[Max];
inline int Read(){
    int X=0;char CH=getchar();bool F=0;
    while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
    while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
    return F?-X:X;
}
inline void Write(int X){
    if(X<0)X=-X,putchar('-');
    if(X>9)Write(X/10);
    putchar(X%10+48);
}
int main(){
    int I,J,K;
    N=Read();
    for(I=1;I<=N;I++){
        A[I]=Read();B[I]=-A[I];
    }
    for(I=1;I<=N;I++){
        DP1[I]=lower_bound(AA+1,AA+1+LenA,A[I])-AA;
        if(AA[DP1[I]]==0){
            LenA++;
        }AA[DP1[I]]=A[I];BB[I]=-Inf;
    }
    for(I=N;I>=1;I--){
        DP2[I]=lower_bound(BB+1,BB+1+LenB,B[I])-BB;
        if(BB[DP2[I]]==-Inf){
            LenB++;
        }BB[DP2[I]]=B[I];
    }
    for(I=1;I<=N;I++){
        if(DP1[I]+DP2[I]-1==LenA){
            Ans[++Ans_Num]=I;Map[Make(DP1[I],DP2[I])]++;
        }
    }
    Write(LenA),putchar('\n');
    for(I=1;I<=Ans_Num;I++){
        if(Map[Make(DP1[Ans[I]],DP2[Ans[I]])]==1){
            Write(Ans[I]),putchar(' ');
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81480434
今日推荐