[kuangbin带你飞]专题十一 网络流

B - Dining

 POJ - 3281 

首先二分图的多重匹配匈牙利算法是不行的,无法最优。

 

 直接在构图跑最大流也是不行的:会出现限制。

 

 每一个人拆成两个点,流量为1,增加流量限制,保证不会变大。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef double db;
const db eps=1e-6;
const int inf=0x3f3f3f3f;
const int N=1e4+5;
int n,m,S,T;
struct Dinic{
    int head[N],dep[N];
    int ecnt; 
    struct edge{
        int v,flow,next;
    }e[N*10];

    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }
    void addedge(int u, int v, int  flow) {
    e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    bool BFS(){//分层图找增广路
        memset(dep,0,sizeof dep);
        queue<int>Q;
        Q.push(S);dep[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=head[u];~i;i=e[i].next){
                if(e[i].flow&&!dep[e[i].v]){
                    dep[e[i].v]=dep[u]+1;
                    Q.push(e[i].v);
                }
            }
        }
        return dep[T];
    }
    int DFS(int u, int f){//推进新流
        if(u==T||f==0)return f;
        int w,used=0;
        for(int i=head[u];~i;i=e[i].next){
            if(e[i].flow&&dep[e[i].v]==dep[u]+1){
                w=DFS(e[i].v,min(f,e[i].flow));//多路增广
                e[i].flow-=w;e[i^1].flow+=w;
                used+=w;f-=w;
            }
        }
        if(!used)dep[u]=-1;//炸点优化
        return used;
    }
    int maxflow() {
        int ans=0;
        while(BFS()){
            ans+=DFS(S,inf);
        }
        return ans;
    }
}dinic; 
int main(){
    int F,D;
    scanf("%d %d %d",&n,&F,&D);
    S=0,T=2*n+F+D+10;
    dinic.init();
    for(int i=1;i<=F;i++){
        dinic.addedge(S,i,1);
        dinic.addedge(i,S,0);
    }
    // for(int i=1;i<=n;i++)
    for(int i=1,A,B,v;i<=n;i++){
        scanf("%d %d",&A,&B);
        while(A--){scanf("%d",&v);dinic.addedge(v,i+F,1);dinic.addedge(i+F,v,0);}
        dinic.addedge(i+F,i+F+n,1);
        dinic.addedge(i+F+n,i+F,0);
        while(B--){scanf("%d",&v);dinic.addedge(i+F+n,v+2*n+F,1);dinic.addedge(v+2*n+F,i+F+n,0);}

    }
    for(int i=1;i<=D;i++){
    dinic.addedge(i+2*n+F,T,1);   
    dinic.addedge(T,i+2*n+F,0);   
    }
    printf("%d\n",dinic.maxflow());

    // system("pause");
    return 0;
}
View Code

D - Going Home

 POJ - 2195 

一张网格图,给定若干个人和房子,求所有人走到房子的最小花费。

把人和房子连起来,跑一遍费用流即可。

#include <cstdio>
// #include<bits/stdc++.h>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e4+50;
int n,m,S,T,maxflow,mincost;
struct Dinic{
    int head[N],dep[N],dis[N],pre[N],flow[N],last[N];
    //dep记录分层图,pre点前驱,flow点的流,last点的前一条边,dis表示费用
    bool inq[N];
    int ecnt; 
    struct edge{
        int v,w,flow,next;
    }e[N*10];
    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }
    void addedge(int u,int v,int flow,int w){
    e[ecnt].v=v;e[ecnt].w=w;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    bool spfa(){//spfa找增广路
        memset(dis,inf,sizeof dis);
        memset(flow,inf,sizeof flow);
        memset(inq,0,sizeof inq);
        queue<int>Q;
        Q.push(S);inq[S]=1;dis[S]=0;pre[T]=-1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();inq[u]=0;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].v,w=e[i].w;
                if(e[i].flow>0&&dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;pre[v]=u;last[v]=i;
                    flow[v]=min(flow[u],e[i].flow);
                    if(!inq[v]){
                    Q.push(v);inq[v]=1;
                    }
                }
            }
        }
        return pre[T]!=-1;
    }
    int  MCMF(){
     maxflow=mincost=0;
        while(spfa()){//推进新流
            int cur=T;
            maxflow+=flow[T];
            mincost+=flow[T]*dis[T];
            while(cur!=S){//汇点向前回溯
                e[last[cur]].flow-=flow[T];
                e[last[cur]^1].flow+=flow[T];
                cur=pre[cur];
            }
        }
        return mincost;
    }
}dinic;  
char gp[500][500];
vector<pair<int,int> >H,M;
int get(int a,int b,int c,int d){
    return abs(a-c)+abs(b-d);
}
int getid(int x,int y){return (x-1)*m+y;}
int main(){
    while(~scanf("%d %d",&n,&m),n+m){
        dinic.init();
        H.clear();
        M.clear();
        for(int i=1;i<=n;i++){
            getchar();
            for(int j=1;j<=m;j++){
                scanf("%c",&gp[i][j]);
                if(gp[i][j]=='m')M.pb(mp(i,j));
                else if(gp[i][j]=='H')H.pb(mp(i,j));
            }
        }
        // cout<<"mp "<<endl;
        // for(int i=1;i<=n;i++){
        //     for(int j=1;j<=m;j++){
        //         cout<<gp[i][j];
        //     }
        //     cout<<endl;
        // }
        // cout<<endl;
        // cout<<"M :"<<M.size()<<endl;
        // cout<<"H "<<H.size()<<endl;
        S=0,T=n*m+10;
        for(int i=0;i<H.size();i++){
            int x=H[i].fi,y=H[i].se;
            int id=getid(x,y);
            dinic.addedge(S,id,1,0);
            dinic.addedge(id,S,0,0);
        }

        for(int i=0;i<H.size();i++){
            int hx=H[i].fi,hy=H[i].se;
            int hs=getid(hx,hy);
            for(int j=0;j<M.size();j++){
            int mx=M[j].fi,my=M[j].se;
            int me=getid(mx,my);
            int cost=get(hx,hy,mx,my);
            dinic.addedge(hs,me,1,cost);
            dinic.addedge(me,hs,0,-cost);
            }
        }
        for(int i=0;i<M.size();i++){
            int x=M[i].fi,y=M[i].se;
            int id=getid(x,y);
            dinic.addedge(id,T,1,0);
            dinic.addedge(T,id,0,0);
        }        
        printf("%d\n",dinic.MCMF());
    }
        // system("pause");
    return 0;
}
View Code

E - Minimum Cost

 POJ - 2516 

m个货店,n个人,每个人需要若干个物品,每个点可以提供若干个物品,求满足所有人需求的最小花费。

开始想直接暴力建 kn 个人点,km个店点,一共有5000个点,TLE

首先特判,如果k个节点提供的小于需要的,直接输出-1。 

考虑k种商品相互独立,互不关联,跑k次费用流,将结果累加即可。

 

#include <cstdio>
// #include<bits/stdc++.h>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+50;
int n,m,K,S,T,maxflow,mincost;
struct Dinic{
    int head[N],dep[N],dis[N],pre[N],flow[N],last[N];
    //dep记录分层图,pre点前驱,flow点的流,last点的前一条边,dis表示费用
    bool inq[N];
    int ecnt; 
    struct edge{
        int v,w,flow,next;
    }e[N*10];
    void init(){
     // for(int i=0;i<K*(m+n);i++)head[i]=-1;
        // memset(head, -1, sizeof head);
        for(int i=0;i<=2*m*n*K*K;i++)head[i]=-1;
        ecnt = 0;
    }
    void addedge(int u,int v,int flow,int w){
    e[ecnt].v=v;e[ecnt].w=w;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    bool spfa(){//spfa找增广路
        memset(dis,inf,sizeof dis);
        memset(flow,inf,sizeof flow);
        memset(inq,0,sizeof inq);
        queue<int>Q;
        Q.push(S);inq[S]=1;dis[S]=0;pre[T]=-1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();inq[u]=0;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].v,w=e[i].w;
                if(e[i].flow>0&&dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;pre[v]=u;last[v]=i;
                    flow[v]=min(flow[u],e[i].flow);
                    if(!inq[v]){
                    Q.push(v);inq[v]=1;
                    }
                }
            }
        }
        return pre[T]!=-1;
    }
    int MCMF(){
     maxflow=mincost=0;
        while(spfa()){//推进新流
            int cur=T;
            maxflow+=flow[T];
            mincost+=flow[T]*dis[T];
            while(cur!=S){//汇点向前回溯
                e[last[cur]].flow-=flow[T];
                e[last[cur]^1].flow+=flow[T];
                cur=pre[cur];
            }
        }
        return mincost;
    }
}dinic;
int need[100],supply[100];
int mt[100][100][100],from[100][100],to[100][100];
bool check(){
    for(int i=1;i<=K;i++)if(supply[i]<need[i])return 0;
    return 1;
} 
int main(){
    while(~scanf("%d %d %d",&n,&m,&K),n+m+K){
        memset(need,0,sizeof need);
        memset(supply,0,sizeof supply);
        for(int i=1;i<=n;i++){
            for(int k=1;k<=K;k++){
                scanf("%d",&to[i][k]);
                need[k]+=to[i][k];
            }
        }
    for(int i=1;i<=m;i++){
        for(int k=1;k<=K;k++){
                scanf("%d",&from[i][k]);
                supply[k]+=from[i][k];
        }
    }
    for(int k=1;k<=K;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&mt[k][i][j]);
            }
        }
    }
    if(!check()){puts("-1");continue;}
    int ans=0;
    for(int k=1;k<=K;k++){
        dinic.init();S=0;T=m+n+50;
        for(int i=1;i<=m;i++){
            dinic.addedge(S,i,from[i][k],0);
            dinic.addedge(i,S,0,0);
        }
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
        dinic.addedge(i,m+j,inf,mt[k][j][i]);
        dinic.addedge(m+j,i,0,-mt[k][j][i]);
                   
            }
        }
        for(int i=1;i<=n;i++){
            dinic.addedge(m+i,T,to[i][k],0);
            dinic.addedge(T,m+i,0,0);
        }
        ans+=dinic.MCMF();
    }
    printf("%d\n",ans);
    }
   // system("pause");
    return 0;
}
View Code

G - Island Transport

 HDU - 4280 

。。看错题了,题目保证流没有相交,

TLE的原因是因为本来是个无向边,非要拆成四条边肯定多了两倍时间 ,实际拆成两条边就行了。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include<bits/stdc++.h>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+50;
int n,m,S,T;
struct Dinic{
    int head[N],dep[N];
    int ecnt; 
    struct edge{
        int v,flow,next;
    }e[N*10];

    void init(){
        memset(head, -1, sizeof head);
        ecnt = 0;
    }

    void addedge(int u, int v, int  flow) {
    e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++;
    }

    bool BFS(){//分层图找增广路
        memset(dep,0,sizeof dep);
        queue<int>Q;
        Q.push(S);dep[S]=1;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=head[u];~i;i=e[i].next){
                if(e[i].flow&&!dep[e[i].v]){
                    dep[e[i].v]=dep[u]+1;
                    Q.push(e[i].v);
                }
            }
        }
        return dep[T];
    }

    int DFS(int u, int f){//推进新流
        if(u==T||f==0)return f;
        int w,used=0;
        for(int i=head[u];~i;i=e[i].next){
            if(e[i].flow&&dep[e[i].v]==dep[u]+1){
                w=DFS(e[i].v,min(f,e[i].flow));//多路增广
                e[i].flow-=w;e[i^1].flow+=w;
                used+=w;f-=w;
            }
        }
        if(!used)dep[u]=-1;//炸点优化
        return used;
    }
    int maxflow() {
        int ans=0;
        while(BFS()){
            ans+=DFS(S,inf);
        }
        return ans;
    }
}dinic;    
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&m);
        int Min=inf,Max=-inf;
        for(int i=1,x,y;i<=n;i++){
            scanf("%d %d",&x,&y);
            if(x>Max){Max=x;T=i;}
            if(x<Min){Min=x;S=i;}
        }
        dinic.init();
        for(int i=1,u,v,f;i<=m;i++){
            scanf("%d %d %d",&u,&v,&f);
            dinic.addedge(u,v,f);
            // dinic.addedge(v,u,0);
            dinic.addedge(v,u,f);
            // dinic.addedge(u,v,0);
               
        }
        printf("%d\n",dinic.maxflow());
    }
   // system("pause");
    return 0;
}
View Code

 

posted @ 2020-05-01 15:14  无声-黑白  阅读(192)  评论(0编辑  收藏  举报