• 「网络流24题」最长不下降子序列问题


    传送门:>Here<

    题意:

    给定正整数序列$x_1,...,x_n$

    (1)计算其最长不下降子序列的长度s。

    (2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。

    (3)如果允许在取出的序列中多次使用$x_1$和$x_n$,则从给定序列中最多可取出多少个长度为$s$的不下降子序列。

    思路分析

    题意首先就很坑:注意第二问中的取出二字,意味着一个数字最多只能存在于一个LIS中。所以才会有第三问的假设

    第一问

    第一问很简单,直接暴力$O(n^2)$就好了

    后面的两问需要借助于网络流。

    第二问

    直接解释原理不清晰也不易懂,先详细阐述如何建模过程:

    首先原序列做LIS,其中$f[i]$表示以$a[i]$为结尾的LIS的最大长度。对序列的每一个数进行拆点,也就是分为$<X_i, Y_i>$。我们要让S到T的每一条路径都是一个LIS,因此若$f[i]=1$,则$X_i$的点连接源点(容量为1,后同),$f[i]=maxs$则$Y_i$连接汇点。其中对于每一个数字,连接有向边$(X_i, Y_i)$。并且在做LIS的过程中,若发现一对数字满足$a[j]<=a[i] 且 f[j]+1=f[i]$,则连有向边$(Y_j, X_i)$。最后做最大流即可

    我们发现这道题又和二分图有一点类似。经过刚才这样的连边之后,任一S到T的路径都是一个LIS,并且容量为1保证了每个点不会被用两次(与二分图原理相同),因此最大流即为方案数

    至于与二分图不同的地方我们发现,我们每个点本身连了边,最后LIS的边还是反过来加的。为什么要这么做呢?这是这道题最大的难点。

    首先分析这种方法的正确性:经过现在这样的建图,Y部分称为了边的起点,而X成为了终点。设有一条边是$(Y_a, X_b)$,则其到达$X_b$以后会顺着自己的边到达$Y_b$,而后如果还存在一条边$(Y_b, X_c)$,它就会继续来到$Y_c$,以此类推最终到达T。其中$(X_i, Y_i)$起到了过渡作用,并且保证了每个点只走一次

    然后来讲这种方法的必要性:为什么正着连边不行呢?原因在于我们源点与汇点的选择。想象一个序列$5,4,3,2,1$,按照目前这种方法自然会跑出最大流为5(每个点直接沿着自己的边走了)。然而如果正着连边自己反着连,那么将会跑出0——我们并没有给它一个跑出去的机会

    第三问

    第三问其实就是在第二问的基础上稍作了一些改动。$x_1和x_n$可以用无限次意味着可以去掉之前给这两个点只能用一次的束缚,因此只需要把源点到1,n到汇点的边的容量改为INF即可。注意由于现在起点是右侧点了,所以$(X_1,Y_1)$和$(X_n,Y_n)$的容量务必也要改成INF

    Code

    细节题。Dinic不要打错了不然完蛋

    另外,再DFS的时候由于我们最初给S设的可增广量为INF,而容量也是INF,这就不太对了,因为源点还有可能连更多的边,INF就不够了。因此第三问的时候容量改为INF/10保险一点

    /*By DennyQi*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define  r  read()
    #define  Max(a,b)  (((a)>(b)) ? (a) : (b))
    #define  Min(a,b)  (((a)<(b)) ? (a) : (b))
    using namespace std;
    typedef long long ll;
    const int MAXN = 10010;
    const int MAXM = 10010;
    const int INF = 1061109567;
    inline int read(){
        int x = 0; int w = 1; register int c = getchar();
        while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
        if(c == '-') w = -1, c = getchar();
        while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w;
    }
    int K,N,M,S,T,x,p,maxs;
    int first[MAXM*2],nxt[MAXM*2],to[MAXM*2],cap[MAXM*2],flow[MAXM*2],num_edge=-1;
    int level[MAXN],cur[MAXN];
    queue <int> q;
    int a[MAXN],f[MAXN];
    inline void add(int u, int v, int c, int f){
        to[++num_edge] = v;
        cap[num_edge] = c;
        flow[num_edge] = f;
        nxt[num_edge] = first[u];
        first[u] = num_edge;
    }
    inline bool BFS(){
        memset(level, 0, sizeof(level));
        while(!q.empty()) q.pop();
        q.push(S);
        level[S] = 1;
        int u,v;
        while(!q.empty()){
            u = q.front(); q.pop();
            for(int i = first[u]; i!=-1; i = nxt[i]){
                v = to[i];
                if(!level[v] && cap[i]-flow[i]>0){
                    level[v] = level[u] + 1;
                    q.push(v);
                }
            }
        }
        return level[T] != 0;
    }
    int DFS(int u, int a){
        if(u == T || a == 0) return a;
        int ans = 0, _f,v;
        for(int& i = cur[u]; i != -1; i = nxt[i]){
            v = to[i];
            if(level[u]+1==level[v] && cap[i]-flow[i]>0){
                _f = DFS(v, Min(a,cap[i]-flow[i]));
                ans += _f, a -= _f;
                flow[i] += _f, flow[i^1] -= _f;
                if(a == 0) break;
            }
        }
        return ans;
    }
    inline void Dinic(){
        int ans = 0;
        while(BFS()){
            for(int i = S; i <= T; ++i) cur[i] = first[i];
            ans += DFS(S, INF*2);
        }
        printf("%d
    ", ans);
    }
    int main(){
        N=r;
        memset(first, -1, sizeof(first));
        S = 1, T = 2*N+2;
        for(int i = 1; i <= N; ++i){
            a[i]=r;
            add(i<<1, i<<1|1, 1, 0);
            add(i<<1|1, i<<1, 0, 0);
        }
        maxs = 0;
        for(int i = 1; i <= N; ++i){
            for(int j = 1; j < i; ++j){
                if(a[j] <= a[i]){
                    f[i] = Max(f[i], f[j]);
                }
            }
            ++f[i];
            maxs = Max(maxs, f[i]);
            for(int j = 1; j < i; ++j){
                if(f[j]+1 == f[i] && a[j] <= a[i]){
                    add(j<<1|1, i<<1, 1, 0);
                    add(i<<1, j<<1|1, 0, 0);
                }
            }
        }
        printf("%d
    ", maxs);
        for(int i = 1; i <= N; ++i){
            if(f[i] == 1){
                add(S, i<<1, 1, 0);
                add(i<<1, S, 0, 0);    
            }
            if(f[i] == maxs){
                add(i<<1|1, T, 1, 0);
                add(T, 1<<1|1, 0, 0);
            }
        }
        Dinic();
        memset(flow, 0, sizeof(flow));
        memset(first, -1, sizeof(first));
        memset(nxt, 0, sizeof(nxt));
        memset(f, 0, sizeof(f));
        S = 1, T = 2*N+2; num_edge = -1;
        for(int i = 1; i <= N; ++i){
            if(i == 1 || i == N){
                add(i<<1, i<<1|1, INF/10, 0);
                add(i<<1|1, i<<1, 0, 0);
                continue;
            }
            add(i<<1, i<<1|1, 1, 0);
            add(i<<1|1, i<<1, 0, 0);
        }
        maxs = 0;
        for(int i = 1; i <= N; ++i){
            for(int j = 1; j < i; ++j){
                if(a[j] <= a[i]){
                    f[i] = Max(f[i], f[j]);
                }
            }
            ++f[i];
            maxs = Max(maxs, f[i]);
            for(int j = 1; j < i; ++j){
                if(f[j]+1 == f[i] && a[j] <= a[i]){
                    add(j<<1|1, i<<1, 1, 0);
                    add(i<<1, j<<1|1, 0, 0);
                }
            }
        }
        for(int i = 1; i <= N; ++i){
            if(f[i] == 1){
                if(i == 1){
                    add(S, i<<1, INF/10, 0);
                    add(i<<1, S, 0, 0);        
                }else{
                    add(S, i<<1, 1, 0);
                    add(i<<1, S, 0, 0);        
                }
            }
            if(f[i] == maxs){
                if(i == N){
                    add(i<<1|1, T, INF/10, 0);
                    add(T, 1<<1|1, 0, 0);
                }else{
                    add(i<<1|1, T, 1, 0);
                    add(T, 1<<1|1, 0, 0);    
                };
            }
        }
        Dinic();
        return 0;
    }
  • 相关阅读:
    Action返回类型
    低成本FPGA中实现动态相位调整
    SERDES高速系统(二)
    SERDES高速系统(一)
    Avalon总线概述
    FPGA热设计
    功耗的挑战
    特性阻抗介绍
    低阻抗电源分配系统
    非理想回路信号衰减
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9424520.html
Copyright © 2020-2023  润新知