[网络流24题]最长不下降子序列问题

[luogu 2766] 最长不下降子序列问题

传送门

第一问:

\(O(n^2)\) 的DP求LIS

为了下面叙述方便,我们将DP过程讲一遍

子状态:dp[i]表示以a[i]结尾的LIS长度

初始条件:dp[i]=1

状态转移方程:\(dp[i]=dp[j]+1(j<i,a[j]\leq a[i])\)


第二问:

我们发现若a[j]加上a[i]可以构成一个不下降子序列,则\(j<i,a[j] \leq a[i]\)

又发现每个元素只能在一个序列中,考虑拆点

建图方法:

原点S=0,T=2n+1

(1)每个点拆成两个点,i向i+n连边,容量1

(2)如果dp[i]=1,则s向i连边,容量1

(3)如果\(j<i,a[j] \leq a[i]\),则j+n向i连边,容量1

(4)如果dp[i]=k(k为LIS长度),则i+n向t连边,容量1

最大流即为答案


第三问:

除了1和n对应节点有关的边容量为INF,其他建图和方案二一样

注意一种特殊情况需要特判

当序列严格递减时,dp[i]=1,s=1,并且不存在i,j满足\(j<i,a[j] \leq a[i]\)

由于dp[1]=1=k,则s会向1连一条INF的边,1向1+n连一条INF的边,1+n向t连一条INF的边,最大流为INF

此时第三问的答案应等于第二问,为n

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 505
#define maxm 500005
#define INF 0x3f3f3f3f
using namespace std;
int n;
struct edge{
    int from;
    int to;
    int next;
    int flow;
}E[maxm<<1];
int sz=1;
int head[maxn*2]; 
void add_edge(int u,int v,int w){
//	printf("%d->%d %d\n",u,v,w); 
    sz++;
    E[sz].from=u;
    E[sz].to=v;
    E[sz].flow=w;
    E[sz].next=head[u];
    head[u]=sz;
    
    sz++;
    E[sz].from=v;
    E[sz].to=u;
    E[sz].flow=0;
    E[sz].next=head[v];
    head[v]=sz;
}

int deep[maxn*2];
bool bfs(int s,int t){
    queue<int>q;
    q.push(s);
    for(int i=s;i<=t;i++) deep[i]=0;
    deep[s]=1;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=E[i].next){
            int y=E[i].to;
            if(E[i].flow&&!deep[y]){
                deep[y]=deep[x]+1;
                q.push(y);
                if(y==t) return 1;
            }
        }
    }
    return 0;
}

int dfs(int x,int t,int minf){
    if(x==t) return minf;
    int k,rest=minf;
    for(int i=head[x];i;i=E[i].next){
        int y=E[i].to;
        if(E[i].flow&&deep[y]==deep[x]+1){
            k=dfs(y,t,min(rest,E[i].flow));
            rest-=k;
            E[i].flow-=k;
            E[i^1].flow+=k;
            if(k==0) deep[y]=0;
            if(rest==0) break;
        }
    }
    return minf-rest;
}

int dinic(int s,int t){
    int nowflow=0,maxflow=0;
    while(bfs(s,t)){
        while(nowflow=dfs(s,t,INF)) maxflow+=nowflow;
    }
    return maxflow;
}

int len;
int a[maxn];
int dp[maxn];
int solve1(){
    int ans=0;
    for(int i=1;i<=n;i++){
        dp[i]=1;
        for(int j=1;j<i;j++){
            if(a[i]>=a[j]){
                dp[i]=max(dp[j]+1,dp[i]);
            }
        }
        ans=max(ans,dp[i]);
    }
    return ans;
}

int solve2(){
    int s=0,t=n*2+1;
    for(int i=1;i<=n;i++){
        if(dp[i]==1) add_edge(s,i,1);
    }
    for(int i=1;i<=n;i++){
        add_edge(i,i+n,1);//限制每个点只被选一次 
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            if(a[i]>=a[j]&&dp[i]==dp[j]+1){
                add_edge(j+n,i,1);
            }
        } 
    }
    for(int i=1;i<=n;i++){
        if(dp[i]==len){
            add_edge(i+n,t,1);
        }
    } 
    return dinic(s,t);
}

bool is_decrease(){
    for(int i=2;i<=n;i++){
        if(a[i]>=a[i-1]) return 0;
    }
    return 1;
}
int solve3(){
    if(is_decrease()) return solve2();
    sz=1;
    memset(E,0,sizeof(E));
    memset(head,0,sizeof(head)); 
    int s=0,t=n*2+1;
    for(int i=1;i<=n;i++){
        if(dp[i]==1){
            if(i==1||i==n) add_edge(s,i,INF);
            else add_edge(s,i,1);
        }
    }
    for(int i=1;i<=n;i++){
        if(i==1||i==n) add_edge(i,i+n,INF);
        else add_edge(i,i+n,1);//限制每个点只被选一次 
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            if(a[i]>=a[j]&&dp[i]==dp[j]+1){
                add_edge(j+n,i,1);
            }
        } 
    }
    for(int i=1;i<=n;i++){
        if(dp[i]==len){
            if(i==1||i==n) add_edge(i+n,t,INF);
            else add_edge(i+n,t,1);
        }
    } 
    return dinic(s,t);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    len=solve1();
    printf("%d\n",len);
    printf("%d\n",solve2());
    printf("%d\n",solve3());
}


posted @ 2019-02-05 16:06  birchtree  阅读(578)  评论(0编辑  收藏  举报