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

    网络流24题

    1.飞行员配对方案问题

    题目链接

    二分图匹配模板题

    #include <bits/stdc++.h>
    using namespace std;
    const int maxnx=1e3+5;
    const int maxny=1e3+5;
    const int maxm=2e6+5;
    int nx,ny,m;
    int my[maxny],mx[maxnx];
    int vis[maxnx];
    struct edge{
        int next,to;
    }E[maxm];
    int tot=0,head[maxnx];
    void addedge(int u,int v){
        E[++tot].to=v;
        E[tot].next=head[u];
        head[u]=tot;
    }
    int find_path(int u){
        if(vis[u])return 0;
        vis[u]=1;
        for(int i=head[u];i>0;i=E[i].next){
            int v=E[i].to;
            if(my[v]==0||find_path(my[v])){
                my[v]=u;
                mx[u]=v;
                return 1;
            }
        }
        return 0;
    }
    int main(){
        int nx,ny,m;
        cin>>nx>>ny;
        int u,v;
        while(scanf("%d%d",&u,&v)){
            if(u==-1&&v==-1)break;
            addedge(u,v);
        }
        int ans=0;
        for(int i=1;i<=nx;i++){
            memset(vis,0,sizeof(vis));//如果nx较大,可以采用i作为标记的方法节约时间
            if(find_path(i)){
                ans++;
            }
        }
        printf("%d
    ",ans);
        for(int i=1;i<=nx;i++){
            if(mx[i]){
                printf("%d %d
    ",i,mx[i]);
            }
        }
    }
    

    2.负载平衡问题

    题目链接

    题意:

    公司有 n个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 n个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。输出最少搬运量。

    思路:

    求出平均数,显然最后所有仓库的货物数量都是平均数。如果点u的货物数比平均数大w,则源点到u有一条容量为w;费用为0的边,如果点u的货物数比平均数小w,则u到汇点有一条容量为w,费用为0的边。相邻两点之间有容量无穷大,费用为1的边,跑费用流即可。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxm=1e3+5;
    const int maxn=105;
    const int INF=1e9;
    struct edge {
        int v,next,w,f;
    } E[maxm];
    int tot=1,head[maxn];//tot必须设为1才能使反向边用^得到
    void addedge(int u,int v,int w,int f) {
        E[++tot].v=v;
        E[tot].w=w;
        E[tot].f=f;
        E[tot].next=head[u];
        head[u]=tot;
    }
    int n,m,s,t;
    int dis[maxn],vis[maxn];
    int q[maxn];
    bool spfa(){
        fill(dis,dis+n+1,INF);
        fill(vis,vis+n+1,0);
        int l=0,r=1;
        q[1]=t;
        dis[t]=0;//反向跑最短路
        while(l!=r){
            int u=q[l=l==n?0:l+1];
            vis[u]=0;
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].v;
                int f=-E[i].f;//当前遍历的是反向的边,费用是负的
                if(E[i^1].w&&dis[v]>dis[u]+f){//反向的边才是流的时候走的边,容量要>0
                    dis[v]=dis[u]+f;
                    if(!vis[v]){
                        if(dis[v]>dis[l+1])q[r=r==n?0:r+1]=v;
                        else q[l]=v,l=l==0?n:l-1;//SLF优化
                        vis[v]=1;
                    }
                }
            }
        }
        return dis[s]<INF;
    }
    int maxf,cost;
    int dfs(int u,int flow){
        if(u==t){
            vis[t]=1;
            return flow;
        }
        int used=0;
        vis[u]=1;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v,w=E[i].w,f=E[i].f;
            if(!vis[v]&&w&&dis[u]==dis[v]+f){//满足增广条件
                int temp=dfs(v,min(w,flow-used));
                if(temp){
                    cost+=temp*f;
                    E[i].w-=temp;
                    E[i^1].w+=temp;
                    used+=temp;
                }
                if(used==flow)break;
            }
        }
        return used;
    }
    void mincmaxf() {
        maxf=cost=0;
        while(spfa()) {
            vis[t]=1;
            while(vis[t]) {
                memset(vis,0,sizeof vis);
                maxf+=dfs(s,1e9);//一直増广直到走不到为止
            }
        }
    }
    void add(int u,int v,int w,int f){
        addedge(u,v,w,f);
        addedge(v,u,0,-f);
    }
    int a[105];
    int main() {
        int nn;
        cin>>nn;
        n=nn+2;s=nn+1;t=nn+2;
        int ave=0;
        for(int i=1;i<=nn;i++){
            scanf("%d",&a[i]);
            ave+=a[i];
        }
        ave/=nn;
        for(int i=1;i<=nn;i++){
            if(a[i]>ave){
                add(s,i,a[i]-ave,0);
            }
            if(a[i]<ave){
                add(i,t,ave-a[i],0);
            }
        }
        for(int i=1;i<=nn-1;i++){
            add(i,i+1,1e9,1);
            add(i+1,i,1e9,1);
        }
        add(1,nn,1e9,1);
        add(nn,1,1e9,1);
        mincmaxf();
        printf("%d
    ",cost);
    }
    
    

    3.运输问题

    题目链接

    题意:

    公司有 m个仓库和 n个零售商店。第 i个仓库有 a个单位的货物;第 j个零售商店需要 b个单位的货物。

    货物供需平衡,即(sumlimits_{i=1}^{m}a_i=sumlimits_{j=1}^{n}b_j)

    从第i个仓库运送每单位货物到第 j 个零售商店的费用为Cij 。

    问使所有货物都运走,总费用最大为多少,最小为多少?

    思路:

    求最小是最小费用最大流模板,求最大只要把边全部变成相反数的再跑一遍即可。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxm=1e5+5;
    const int maxn=205;
    const int INF=1e9;
    struct edge {
        int v,next,w,f;
    } E[maxm];
    int tot=1,head[maxn];//tot必须设为1才能使反向边用^得到
    void addedge(int u,int v,int w,int f) {
        E[++tot].v=v;
        E[tot].w=w;
        E[tot].f=f;
        E[tot].next=head[u];
        head[u]=tot;
    }
    int n,m,s,t;
    int dis[maxn],vis[maxn];
    int q[maxn];
    bool spfa(){
        fill(dis,dis+n+1,INF);
        fill(vis,vis+n+1,0);
        int l=0,r=1;
        q[1]=t;
        dis[t]=0;//反向跑最短路
        while(l!=r){
            int u=q[l=l==n?0:l+1];
            vis[u]=0;
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].v;
                int f=-E[i].f;//当前遍历的是反向的边,费用是负的
                if(E[i^1].w&&dis[v]>dis[u]+f){//反向的边才是流的时候走的边,容量要>0
                    dis[v]=dis[u]+f;
                    if(!vis[v]){
                        if(dis[v]>dis[l+1])q[r=r==n?0:r+1]=v;
                        else q[l]=v,l=l==0?n:l-1;//SLF优化
                        vis[v]=1;
                    }
                }
            }
        }
        return dis[s]<INF;
    }
    int maxf,cost;
    int dfs(int u,int flow){
        if(u==t){
            vis[t]=1;
            return flow;
        }
        int used=0;
        vis[u]=1;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v,w=E[i].w,f=E[i].f;
            if(!vis[v]&&w&&dis[u]==dis[v]+f){//满足增广条件
                int temp=dfs(v,min(w,flow-used));
                if(temp){
                    cost+=temp*f;
                    E[i].w-=temp;
                    E[i^1].w+=temp;
                    used+=temp;
                }
                if(used==flow)break;
            }
        }
        return used;
    }
    void mincmaxf() {
        maxf=cost=0;
        while(spfa()) {
            vis[t]=1;
            while(vis[t]) {
                memset(vis,0,sizeof vis);
                maxf+=dfs(s,1e9);//一直増广直到走不到为止
            }
        }
    }
    void add(int u,int v,int w,int f){
        addedge(u,v,w,f);
        addedge(v,u,0,-f);
    }
    int a[105],b[105],c[105][105];
    int main() {
        int nn,mm;
        cin>>mm>>nn;
        n=nn+mm+2;
        s=nn+mm+1;
        t=nn+mm+2;
        for(int i=1;i<=mm;i++){
            scanf("%d",&a[i]);
            add(s,i,a[i],0);
        }
        for(int i=1;i<=nn;i++){
            scanf("%d",&b[i]);
            add(mm+i,t,b[i],0);
        }
        for(int i=1;i<=mm;i++){
            for(int j=1;j<=nn;j++){
                scanf("%d",&c[i][j]);
                add(i,mm+j,1e9,c[i][j]);
            }
        }
        mincmaxf();
        printf("%d
    ",cost);
        //重新建立费用为相反数的模型
        tot=1;
        memset(head,0,sizeof(head));
        for(int i=1;i<=mm;i++){
            add(s,i,a[i],0);
        }
        for(int i=1;i<=nn;i++){
            add(mm+i,t,b[i],0);
        }
        for(int i=1;i<=mm;i++){
            for(int j=1;j<=nn;j++){
                add(i,mm+j,1e9,-c[i][j]);
            }
        }
        mincmaxf();
        printf("%d
    ",-cost);
    }
    

    4.分配问题

    题目链接

    和第三题一样,最小费用最大流模板用两次。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxm=1e5+5;
    const int maxn=1e3+5;
    const int INF=1e9;
    struct edge {
        int v,next,w,f;
    } E[maxm];
    int tot=1,head[maxn];//tot必须设为1才能使反向边用^得到
    void addedge(int u,int v,int w,int f) {
        E[++tot].v=v;
        E[tot].w=w;
        E[tot].f=f;
        E[tot].next=head[u];
        head[u]=tot;
    }
    int n,m,s,t;
    int dis[maxn],vis[maxn];
    int q[maxn];
    bool spfa(){
        fill(dis,dis+n+1,INF);
        fill(vis,vis+n+1,0);
        int l=0,r=1;
        q[1]=t;
        dis[t]=0;//反向跑最短路
        while(l!=r){
            int u=q[l=l==n?0:l+1];
            vis[u]=0;
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].v;
                int f=-E[i].f;//当前遍历的是反向的边,费用是负的
                if(E[i^1].w&&dis[v]>dis[u]+f){//反向的边才是流的时候走的边,容量要>0
                    dis[v]=dis[u]+f;
                    if(!vis[v]){
                        if(dis[v]>dis[l+1])q[r=r==n?0:r+1]=v;
                        else q[l]=v,l=l==0?n:l-1;//SLF优化
                        vis[v]=1;
                    }
                }
            }
        }
        return dis[s]<INF;
    }
    int maxf,cost;
    int dfs(int u,int flow){
        if(u==t){
            vis[t]=1;
            return flow;
        }
        int used=0;
        vis[u]=1;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v,w=E[i].w,f=E[i].f;
            if(!vis[v]&&w&&dis[u]==dis[v]+f){//满足增广条件
                int temp=dfs(v,min(w,flow-used));
                if(temp){
                    cost+=temp*f;
                    E[i].w-=temp;
                    E[i^1].w+=temp;
                    used+=temp;
                }
                if(used==flow)break;
            }
        }
        return used;
    }
    void mincmaxf() {
        maxf=cost=0;
        while(spfa()) {
            vis[t]=1;
            while(vis[t]) {
                memset(vis,0,sizeof vis);
                maxf+=dfs(s,1e9);//一直増广直到走不到为止
            }
        }
    }
    void add(int u,int v,int w,int f){
        addedge(u,v,w,f);
        addedge(v,u,0,-f);
    }
    int c[105][105];
    int main() {
        int nn;
        cin>>nn;
        s=nn+nn+1;
        t=nn+nn+2;
        n=nn+nn+2;
        for(int i=1;i<=nn; i++) {
            for(int j=1;j<=nn;j++){
                scanf("%d",&c[i][j]);
                add(i,nn+j,1,c[i][j]);
            }
        }
        for(int i=1;i<=nn;i++){
            add(s,i,1,0);
            add(nn+i,t,1,0);
        }
        mincmaxf();
        printf("%d
    ",cost);
        tot=1;
        memset(head,0,sizeof(head));
        for(int i=1;i<=nn; i++) {
            for(int j=1;j<=nn;j++){
                add(i,nn+j,1,-c[i][j]);
            }
        }
        for(int i=1;i<=nn;i++){
            add(s,i,1,0);
            add(nn+i,t,1,0);
        }
        mincmaxf();
        printf("%d
    ",-cost);
    }
    

    5.试题库问题

    题目链接

    有n道题,k种题目类型。从n道题选出m道组成一套试卷。给出k个要求,即每种类型的题目要达到ki个((sum ki=m))。一道题可以代表多种类型,但是在试卷中一题只能选择一种类型。问是否有满足要求的方案,若有将其输出(每种类型的题有哪几道)

    显然是一个最大流模型,源点到每道题有一条容量为1的边,每道题到对应类型有一条容量为1的边,每个类型i到汇点有一条容量为ki的边。若最大流不等于(sum ki),则没有符合要求的方案,若等于则有。

    遍历n道题的边(正向边),若u的一条边容量为0,指向v,则说明uv被匹配,将u加入v的答案中即可。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxm=2e5+5;
    const int maxn=1e4+5;
    struct edge{
        int to,next,w;
    }E[maxm];
    int tot=1,head[maxn],cur[maxn];
    void addedge(int u,int v,int w){
        E[++tot].to=v;
        E[tot].w=w;
        E[tot].next=head[u];
        head[u]=tot;
    }
    int n,m,s,t;
    int dep[maxn],q[maxn];
    bool bfs(){
        fill(dep,dep+1+n,0);
        int l=0,r=1;
        q[r]=s;
        dep[s]=1;
        while(l!=r){
            int u=q[l=l==n?0:l+1];
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].to;
                if(E[i].w&&!dep[v]){
                    dep[v]=dep[u]+1;
                    q[r=r==n?0:r+1]=v;
                }
            }
        }
        return dep[t]>0;
    }
    int dfs(int u,int in){
        if(u==t)
            return in;
        int out=0;
        for(int i=cur[u];i&&in;i=E[i].next){
            cur[u]=i;//当前弧优化
            int v=E[i].to;
            if(E[i].w&&dep[v]==dep[u]+1){
                int res=dfs(v,min(E[i].w,in));
                E[i].w-=res;
                E[i^1].w+=res;
                in-=res;
                out+=res;
            }
        }
        if(out==0)dep[u]=-1;//该点无法到达终点,删去
        return out;
    }
    int maxf;
    void dinic(){
        maxf=0;
        while(bfs()){
            for(int i=1;i<=n;i++)cur[i]=head[i];//当前弧初始化
            maxf+=dfs(s,2e9);
        }
    }
    void add(int u,int v,int w){
        addedge(u,v,w);
        addedge(v,u,0);
    }
    int ki[maxn];
    int main(){
        int nn,kk;
        cin>>kk>>nn;
        s=nn+kk+1;
        t=nn+kk+2;
        n=nn+kk+2;
        int sum=0;
        for(int i=1;i<=kk;i++){
            scanf("%d",&ki[i]);
            sum+=ki[i];
            add(nn+i,t,ki[i]);
        }
        for(int i=1;i<=nn;i++){
            int p;
            scanf("%d",&p);
            add(s,i,1);
            while(p--){
                int typ;
                scanf("%d",&typ);
                add(i,nn+typ,1);
            }
        }
        dinic();
        if(maxf!=sum){
            printf("No Solution!
    ");
            return 0;
        }
        set<int>ans[25];
        for(int i=1;i<=nn;i++){
            for(int j=head[i];j;j=E[j].next){
                int w=E[j].w,v=E[j].to;
                if(!w){
                    ans[v-nn].insert(i);
                    break;
                }
            }
        }
        for(int i=1;i<=kk;i++){
            printf("%d:",i);
            for(auto x:ans[i]){
                printf(" %d",x);
            }
            puts("");
        }
    }
    
  • 相关阅读:
    洗礼灵魂,修炼python(20)--自定义函数(1)—基础概念
    洗礼灵魂,修炼python(19)--文件I/O操作,linecache,fileinput模块
    洗礼灵魂,修炼python(18)--温故加知新
    洗礼灵魂,修炼python(17)--跨平台操作三剑客—os,os.path.sys模块
    洗礼灵魂,修炼python(16)--列表进阶话题—>上节作业讲解+copy模块,浅拷贝,深拷贝
    洗礼灵魂,修炼python(15)--列表进阶话题—>列表解析/列表生成器
    洗礼灵魂,修炼python(14)--模块decimal, fractions,operator,collections以及精度介绍
    洗礼灵魂,修炼python(13)--模块random,math,pickle
    748. Shortest Completing Word
    542. 01 Matrix
  • 原文地址:https://www.cnblogs.com/ucprer/p/11850924.html
Copyright © 2011-2022 走看看