zoukankan      html  css  js  c++  java
  • 网络流24题

    网络流24题


    说在前边

    1. 一直没有完整的刷过这套题,打算最近一点点刷掉
    2. 通过《最小割模型在信息学竞赛中的应用》及《浅析一类最小割问题》学习常规建图技巧

    飞行员配对方案问题

    二分图最大匹配

    #include <bits/stdc++.h>
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int M = 100005;
    const int N = 202;
    using namespace std;
    int m,n;
    struct edge{int e,nxt,w;}E[M<<1];
    int h[N],cc;
    void add(int u,int v,int w) {
        E[cc].e=v;E[cc].w=w;E[cc].nxt=h[u];h[u]=cc;++cc;
        E[cc].e=u;E[cc].w=0;E[cc].nxt=h[v];h[v]=cc;++cc;
    }
    int d[N],q[M],st,ed;
    int bfs() {
        int l=0,r=0;
        for(int i=1;i<=n+2;++i) d[i]=0;
        q[r]=st;++r;d[st]=1;
        while(l<r) {
            int u=q[l];++l;
            for(int i=h[u];~i;i=E[i].nxt) {
                int v=E[i].e;
                if(!d[v]&&E[i].w) {
                    d[v]=d[u]+1;
                    q[r]=v;++r;
                    if(v==ed)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int u,int fl) {
        if(u==ed)return fl;
        int s=fl,t;
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(d[v]==d[u]+1&&E[i].w&&s) {
                t=dfs(v,min(E[i].w,s));
                s-=t;
                E[i].w-=t;E[i^1].w+=t;
                if(s==0)return fl;
            }
        }
        if(s==fl) d[u]=0;
        return fl-s;
    }
    int dinic() {
        int ans=0;
        while(bfs())ans+=dfs(st,inf);
        return ans;
    }
    int main() {
        scanf("%d%d",&m,&n);
        for(int i=1;i<=n+2;++i) h[i]=-1;
        st=n+1;ed=n+2;
        int u,v;
        while(scanf("%d%d",&u,&v)!=EOF) {
            if(u==-1&&v==-1)break;
            add(u,v,1);
        }
        for(int i=1;i<=m;++i) add(st,i,1);
        for(int i=m+1;i<=n;++i) add(i,ed,1);
        printf("%d
    ",dinic());
        int f=0;
        for(int i=1;i<=m;++i) {
            for(int j=h[i];~j;j=E[j].nxt) {
                if(E[j].w==0&&E[j].e<=n)printf("%d %d
    ",i,E[j].e),f=1;
            }
        }
        if(!f) puts("No Solution!");
        return 0;
    }
    
    

    太空飞行计划问题

    最大权闭合子图,正点权连源,负点权连汇,点权的绝对值作为边权。原本图中的边容量为无穷。割集将图的源汇分开,与源点相通的点就是最大权闭合子图的点集。

    #include <bits/stdc++.h>
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int M = 100005;
    const int N = 500;
    using namespace std;
    int m,n;
    struct edge{int e,nxt,w;}E[M<<1];
    int h[N],cc;
    void add(int u,int v,int w) {
        E[cc].e=v;E[cc].w=w;E[cc].nxt=h[u];h[u]=cc;++cc;
        E[cc].e=u;E[cc].w=0;E[cc].nxt=h[v];h[v]=cc;++cc;
    }
    int d[N],q[M],st,ed;
    int bfs() {
        int l=0,r=0;
        for(int i=1;i<=n+m+2;++i) d[i]=0;
        q[r]=st;++r;d[st]=1;
        while(l<r) {
            int u=q[l];++l;
            for(int i=h[u];~i;i=E[i].nxt) {
                int v=E[i].e;
                if(!d[v]&&E[i].w) {
                    d[v]=d[u]+1;
                    q[r]=v;++r;
                    if(v==ed)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int u,int fl) {
        if(u==ed)return fl;
        int s=fl,t;
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(d[v]==d[u]+1&&E[i].w&&s) {
                t=dfs(v,min(E[i].w,s));
                s-=t;
                E[i].w-=t;E[i^1].w+=t;
                if(s==0)return fl;
            }
        }
        if(s==fl) d[u]=0;
        return fl-s;
    }
    int dinic() {
        int ans=0;
        while(bfs())ans+=dfs(st,inf);
        return ans;
    }
    vector<int> v,v1,v2;
    int vis[N];
    void dfs2(int u) {
        vis[u]=1;
        if(u>=1&&u<=m)v1.push_back(u);
        if(u>m&&u<=n+m) v2.push_back(u-m);
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(!vis[v]&&E[i].w>0)dfs2(v);
        }
    }
    int a[N],b[N];
    int main() {
        scanf(" %d %d ",&m,&n);
        memset(h,-1,sizeof(h));
        st=m+n+1;ed=st+1;
        int ans = 0;
        for(int i=1;i<=m;++i) {
            string s;v.clear();
            getline(cin,s);int c=0;
            while(1){
                int tmp = 0;
                while(s[c]>='0'&&s[c]<='9'&&c<s.size()) tmp=tmp*10+s[c]-'0',++c;
                v.push_back(tmp);
                if(c==s.size()) break;
                while(s[c]<'0'||s[c]>'9'&&c<s.size()) ++c;
            }
            add(st,i,v[0]);
            ans+=v[0];
            a[i]=v[0];
            for(int j=1;j<v.size();++j) {
                add(i,v[j]+m,inf);
            }
        }
        for(int i=1;i<=n;++i) {int x;
            scanf("%d",&x);b[i]=x;
            add(i+m,ed,x);
        }
    
        ans-=dinic();
    
        dfs2(st);
        int t=0;
        for(auto x: v1)printf("%d ",x),t+=a[x];puts("");
        for(auto x: v2)printf("%d ",x),t-=b[x];puts("");
        printf("%d
    ",ans);
        return 0;
    }
    
    

    最小路径覆盖问题

    DAG最小路径覆盖,拆点二分图最大匹配。分析见这里

    #include <bits/stdc++.h>
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int M = 1000005;
    const int N = 1500;
    using namespace std;
    int m,n;
    struct edge{int e,nxt,w;}E[M<<1];
    int h[N],cc;
    void add(int u,int v,int w) {
        E[cc].e=v;E[cc].w=w;E[cc].nxt=h[u];h[u]=cc;++cc;
        E[cc].e=u;E[cc].w=0;E[cc].nxt=h[v];h[v]=cc;++cc;
    }
    int d[N],q[M],st,ed;
    int bfs() {
        int l=0,r=0;
        for(int i=1;i<=n+n+2;++i) d[i]=0;
        q[r]=st;++r;d[st]=1;
        while(l<r) {
            int u=q[l];++l;
            for(int i=h[u];~i;i=E[i].nxt) {
                int v=E[i].e;
                if(!d[v]&&E[i].w) {
                    d[v]=d[u]+1;
                    q[r]=v;++r;
                    if(v==ed)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int u,int fl) {
        if(u==ed)return fl;
        int s=fl,t;
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(d[v]==d[u]+1&&E[i].w&&s) {
                t=dfs(v,min(E[i].w,s));
                s-=t;
                E[i].w-=t;E[i^1].w+=t;
                if(s==0)return fl;
            }
        }
        if(s==fl) d[u]=0;
        return fl-s;
    }
    int dinic() {
        int ans=0;
        while(bfs())ans+=dfs(st,inf);
        return ans;
    }
    int vis[N],in[N];
    vector<int> G[N];
    void dfs2(int u) {
        vis[u]=1;
        printf("%d ",u);
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(v>n&&v<=2*n&&!vis[v-n])--in[v-n];
        }
        for(int i=h[u];~i;i=E[i].nxt)if(!E[i].w){
            int v=E[i].e;
            if(v>n&&v<=2*n&&!vis[v-n])dfs2(v-n);
        }
    }
    
    int main() {
        scanf(" %d %d ",&n,&m);
        memset(h,-1,sizeof(h));
        st = 2*n+1; ed = st+1;
        for(int i=1;i<=n;++i) add(st,i,1);
        for(int i=1;i<=n;++i) add(i+n,ed,1);
        for(int i=1;i<=m;++i) {int x,y;
            scanf("%d%d",&x,&y);
            add(x,y+n,1);
            ++in[y];
        }
        int ans = n - dinic();
    
        for(int i=1;i<=n;++i)if(!in[i]&&!vis[i]) {
            dfs2(i);puts("");
        }
        printf("%d
    ",ans);
        return 0;
    }
    
    

    魔术球问题

    每根柱子看作是一条链,那么就是用最少的链把所有点都覆盖掉,及最小路径覆盖。在dinic跑的同时,加入新的点,直到超过n条链结束。其余同上题。

    
    #include <bits/stdc++.h>
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int M = 1000005;
    const int lim = 5000;
    using namespace std;
    int m,n;
    struct edge{int e,nxt,w;}E[M<<2];
    int h[lim<<2],cc;
    void add(int u,int v,int w) {
        E[cc].e=v;E[cc].w=w;E[cc].nxt=h[u];h[u]=cc;++cc;
        E[cc].e=u;E[cc].w=0;E[cc].nxt=h[v];h[v]=cc;++cc;
    }
    int d[lim<<2],q[M],st,ed;
    int bfs() {
        int l=0,r=0;
        memset(d,0,sizeof(d));
        q[r]=st;++r;d[st]=1;
        while(l<r) {
            int u=q[l];++l;
            for(int i=h[u];~i;i=E[i].nxt) {
                int v=E[i].e;
                if(!d[v]&&E[i].w) {
                    d[v]=d[u]+1;
                    q[r]=v;++r;
                    if(v==ed)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int u,int fl) {
        if(u==ed)return fl;
        int s=fl,t;
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(d[v]==d[u]+1&&E[i].w&&s) {
                t=dfs(v,min(E[i].w,s));
                s-=t;
                E[i].w-=t;E[i^1].w+=t;
                if(s==0)return fl;
            }
        }
        if(s==fl) d[u]=0;
        return fl-s;
    }
    
    int vis[lim<<2],in[lim<<2];
    
    void dfs2(int u,int cnt) {
        vis[u]=1;
        printf("%d ",u);
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(v<cnt+lim&&v>lim&&!vis[v-n])--in[v-n];
        }
        for(int i=h[u];~i;i=E[i].nxt)if(!E[i].w){
            int v=E[i].e;
            if(v<cnt+lim&&v>lim&&!vis[v-lim])dfs2(v-lim,cnt);
        }
    }
    int is2[M];
    int main() {
        scanf("%d",&n);
        for(int i=1;i*i<=100000;++i)is2[i*i]=1;
        memset(h,-1,sizeof(h));
        int cnt=0;
        st = lim*2+2; ed  = lim*2+3;
        int ans=0;
        for(int i=1;i<=lim;++i) add(st,i,1);
        for(int i=1;i<=lim;++i) add(i+lim,ed,1);
        while(1){
            ++cnt;
            for(int i=1;i<cnt;++i)if(is2[i+cnt]){
                add(i,cnt+lim,1);
                ++in[cnt];
            }
    
            while(bfs())ans+=dfs(st,inf);
    
            int tmp = cnt - ans;
            if(tmp==n+1)break;
        }
        printf("%d
    ",cnt-1);
        for(int i=1;i<cnt;++i)if(!vis[i]) {
            dfs2(i,cnt);puts("");
        }
        return 0;
    }
    
    

    圆桌问题

    二分图多重匹配,(总点数在变化。。。贴板子WA了几发

    #include <bits/stdc++.h>
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int M = 1000005;
    const int N = 500;
    using namespace std;
    int m,n;
    struct edge{int e,nxt,w;}E[M<<1];
    int h[N],cc;
    void add(int u,int v,int w) {
        E[cc].e=v;E[cc].w=w;E[cc].nxt=h[u];h[u]=cc;++cc;
        E[cc].e=u;E[cc].w=0;E[cc].nxt=h[v];h[v]=cc;++cc;
    }
    int d[N],q[M],st,ed;
    int bfs() {
        int l=0,r=0;
        for(int i=1;i<=n+m+2;++i) d[i]=0;
        q[r]=st;++r;d[st]=1;
        while(l<r) {
            int u=q[l];++l;
            for(int i=h[u];~i;i=E[i].nxt) {
                int v=E[i].e;
                if(!d[v]&&E[i].w) {
                    d[v]=d[u]+1;
                    q[r]=v;++r;
                    if(v==ed)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int u,int fl) {
        if(u==ed)return fl;
        int s=fl,t;
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(d[v]==d[u]+1&&E[i].w&&s) {
                t=dfs(v,min(E[i].w,s));
                s-=t;
                E[i].w-=t;E[i^1].w+=t;
                if(s==0)return fl;
            }
        }
        if(s==fl) d[u]=0;
        return fl-s;
    }
    int dinic() {
        int ans=0;
        while(bfs())ans+=dfs(st,inf);
        return ans;
    }
    
    int a[N],b[N];
    int main() {
        scanf("%d%d",&m,&n);
        memset(h,-1,sizeof(h));
        st = m + n +1; ed = st+1;
        int tmp = 0;
        for(int i=1;i<=m;++i) {
            scanf("%d",&a[i]);
            add(st,i,a[i]);
            tmp+=a[i];
        }
        for(int i=1;i<=n;++i) {
            scanf("%d",&b[i]);
            add(i+m,ed,b[i]);
        }
        for(int i=1;i<=m;++i)
            for(int j=1;j<=n;++j) add(i,j+m,1);
        int ans = dinic();
        if(ans==tmp) {
            puts("1");
            for(int i=1;i<=m;++i) {
                for(int j=h[i];~j;j=E[j].nxt)if(E[j].e>m&&E[j].e<=n+m&&!E[j].w) printf("%d ",E[j].e-m);
                puts("");
            }
        }
        else puts("0");
        return 0;
    }
    
    

    最长递增子序列问题

    第一问直接dp即可,第二问,先发现它是在第一问最长上升子序列的基础上做的。一眼的思路就是,我在dp的时候把以每个位置为最大值的,所有的最长上升子序列,最后取出其中的所有最长上升子序列,然后每个序列按顺序从左到右连边,每个点限流为1,然后直接最大流。这样做第二问应该是ok的。但是一开始用vector< vector > 存了所有的序列,可能爆内存吧。第三问用同样的方法,然后把1和n的限流改成了inf,如果他们和源汇相连也改成inf,还没搞清楚为什么会WA。其实不用求出所有序列,只要确定开头和末尾,中间把他们有转移关系的位置连边,限流,再最大流。第三问跟上面操作一样吧。

    #include <bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;++i)
    #define per(i,a,b) for(int i=a;i>=b;--i)
    #define pb push_back
    const int N = 844;
    const int inf = 0x3f3f3f3f;
    typedef long long ll;
    typedef unsigned long long ull;
    using namespace std;
    int n, ans1, ans2, ans3;
    struct edge{int e,w,nxt;}E[N*N*2];
    int cc, h[N];
    void add(int u, int v, int w) {
        E[cc].e=v;E[cc].w=w;E[cc].nxt=h[u];h[u]=cc;++cc;
        E[cc].e=u;E[cc].w=0;E[cc].nxt=h[v];h[v]=cc;++cc;
    }
    int st, ed, d[N], q[N*N*2], in[N], out[N];
    int bfs() {
        int l=0,r=0;
        for(int i=1;i<=2*n+3;++i)d[i]=0;
        q[r]=st;++r;d[st]=1;
        while(l < r) {
            int u = q[l]; ++l;
            for(int i=h[u];~i;i=E[i].nxt) {
                int v=E[i].e;
                if(!d[v]&&E[i].w) {
                    d[v]=d[u]+1;
                    q[r]=v;++r;
                    if(v==ed)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int u, int fl) {
        if(u==ed) return fl;
        int s=fl,t;
        for(int i=h[u];~i;i=E[i].nxt) {
            int v=E[i].e;
            if(d[v]==d[u]+1&&E[i].w&&s) {
                t=dfs(v,min(E[i].w,s));
                s-=t;
                E[i].w-=t;
                E[i^1].w+=t;
                if(s==0)return fl;
            }
        }
        if(s==fl)d[u]=0;
        return fl-s;
    }
    int dinic() {
        int ans = 0;
        while(bfs()) ans+=dfs(st,inf);
        return ans;
    }
    int a[N], dp[N];
    int main() {
        scanf("%d",&n);
        rep(i,1,n) scanf("%d",&a[i]);
        // 1
        rep(i,1,n)dp[i]=1;
        rep(i,2,n)rep(j,1,i-1) 
            if(a[j]<a[i])dp[i] = max(dp[i],dp[j]+1);
        rep(i,1,n) ans1=max(ans1,dp[i]);
        printf("%d
    ",ans1);
        // 2
        st=n*2+1; ed=st+1;
        rep(i,1,ed) h[i]=-1;
        rep(i,1,n) add(i,i+n,1);
        rep(i,1,n) if(dp[i] == 1) add(st,i,1);
        rep(i,1,n) if(dp[i] == ans1) add(i+n,ed,1);
        rep(i,1,n) rep(j,1,i)
            if(a[i]>a[j]&&dp[i]==dp[j]+1) add(j+n,i,1);
        int ans2 = dinic();
        printf("%d
    ",ans2);
        //3
        add(1,1+n,inf);
        add(st,1,inf);
        add(n,n+n,inf);
        if(dp[n]==ans1) add(n+n,ed,inf);
        int ans3 = ans2 + dinic();
        printf("%d
    ",ans3);
        return 0;
    }
    
  • 相关阅读:
    动态代理,反射的用途及实现
    谈一谈web.xml中的context-param和init-param
    后端程序员需要了解的前端知识(持续更新中)
    angularJS要点记录,$location,$http等等
    HTTP1.0和HTTP2.0的区别,以及HTTP和HTTPS的区别
    浅谈Fork/Join框架
    ConcurrentHashMap 的工作原理及源码分析,如何统计所有的元素个数
    HTTP协议常见的状态码
    图解HTTP,状态码,TCP、UDP等网络协议相关总结(持续更新)
    jmeter(五)JDBC Request
  • 原文地址:https://www.cnblogs.com/RRRR-wys/p/9326985.html
Copyright © 2011-2022 走看看