• [网络流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
    ",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
    ",len);
        printf("%d
    ",solve2());
        printf("%d
    ",solve3());
    }
    
    
    
  • 相关阅读:
    sqlite3经常使用命令&amp;语法
    DataTable.AcceptChanges方法有何用处
    C面试题
    keil程序在外部RAM中调试的问题总结(个人的一点经验总结)
    jps命令
    【C++基础】类的组合
    《算法导论》为什么经典
    Spring3.0 AOP 具体解释
    Java中StringBuilder的清空方法比較
    TFS(Team Foundation Server)介绍和入门
  • 原文地址:https://www.cnblogs.com/birchtree/p/10352819.html
Copyright © 2020-2023  润新知