1. 程式人生 > >訓練指南 網絡流題集

訓練指南 網絡流題集

i++ turn 最大利潤 這樣的 一個數 live 兩個 成本 ice


layout: post
title: 訓練指南 網絡流題集
author: "luowentaoaa"
catalog: true
mathjax: true
tags:
- 網絡流
- 圖論
- 訓練指南


A.UVA - 11248 (最大流,最小割)

UVA - 11248 Frequency Hopping

題意

給定一個有向網絡,每條邊均有一個容量。問是否存在一個從點1到點N,流量為C的流。如果不存在,是否可以恰好修改一條弧的容量,使得存在這樣的流。

思路

先求一遍最大流,如果大於等於C,那麽就直接輸出possible。

否則的話就是最大流達不到C,那麽對哪些邊進行擴容呢,肯定是選擇最小割!

將最小割的邊集全部求出來,之後每條邊都嘗試將容量變為C,看看能否達到要求。

優化一:求完最大流後把流量留著,以後每次在它的基礎上增廣。

優化二:每次沒必要求出最大流,增廣到流量至少為C時就可以停下來。

搞不到為什麽劉汝佳代碼那麽快

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e6+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
struct Edge{
    int from,to,cap,flow;
    Edge(int u,int v,int c,int f)
    :from(u),to(v),cap(c),flow(f){}
    bool operator<(const Edge& a)const{
        return from<a.from||(from==a.from&&to<a.to);
    }
};
struct Dinic{
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];
    void init(int n){
        this->n=n;
        for(int i=0;i<n;i++)G[i].clear();
        edges.clear();
    }
    void ClearFlow(){
        for(int i=0;i<edges.size();i++)edges[i].flow=0;
    }
    void AddEdge(int from,int to,int cap){
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BFS(){
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        queue<int>q;
        q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge& e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a){
        if(x==t||a==0)return a;
        int flow=0,f;
        for(int &i=cur[x];i<G[x].size();i++){
            Edge& e=edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)break;
            }
        }
        return flow;
    }
    int Maxflow(int s,int t){
        this->s=s;this->t=t;
        int flow=0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            flow+=DFS(s,inf);
        }
        return flow;
    }
    vector<int>Mincut(){ /// call this after maxflow
        vector<int>ans;
        for(int i=0;i<edges.size();i++){
            Edge& e=edges[i];
            if(vis[e.from]&&!vis[e.to]&&e.cap>0)ans.push_back(i);
        }
        return ans;
    }
    void Reduce(){
        for(int i=0;i<edges.size();i++)edges[i].cap-=edges[i].flow;
    }
}g;

int main()
{
    int n,e,c,kase=0;
    while(scanf("%d%d%d",&n,&e,&c)==3&&n){
        g.init(n);
        while(e--){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            g.AddEdge(a-1,b-1,c);
        }
        int flow=g.Maxflow(0,n-1);
        printf("Case %d: ",++kase);
        if(flow>=c)printf("possible\n");
        else{
            vector<int>cut=g.Mincut();
            g.Reduce();
            vector<Edge>ans;
            for(int i=0;i<cut.size();i++){
                Edge& e=g.edges[cut[i]];
                e.cap=c;
                g.ClearFlow();
                if(flow+g.Maxflow(0,n-1)>=c)ans.push_back(e);
                e.cap=0;
            }
            if(ans.empty())printf("not possible\n");
            else{
                sort(ans.begin(),ans.end());
                printf("possible option:(%d,%d)", ans[0].from+1, ans[0].to+1);
                for(int i = 1; i < ans.size(); i++)
                printf(",(%d,%d)", ans[i].from+1, ans[i].to+1);
                printf("\n");
            }
        }
    }
    return 0;
}

B.UVALive - 2531 (構圖最大流)

題意

有 n 個隊伍進行比賽,每個隊伍比賽數目是一樣的,每場恰好一個勝一個負,給定每個隊伍當前勝的場數敗的數目,以及兩個隊伍剩下的比賽場數,問你冠軍隊伍可能是哪些隊。

思路

對每個隊伍 i 進行判斷是不是能冠軍,最優的情況的就是剩下的比賽全都勝,也就是一共勝的數目就是剩下的要比賽的數再加上原來勝的數目sum,然後把每兩個隊伍比賽看成一個結點,(u, v),然後從 s 向 結點加一條容量要打的比賽數目的容量,然後從 (u, v) 向 u 和 v 分別加一條容量為無窮大的邊,然後每個 u 向 t 加一條容量為 sum - w[i] ,跑一個最大流,如果是滿流是,那麽就是有解,也就是 i 可能是冠軍。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=700+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
struct Edge{
    int from,to,cap,flow;
    Edge(int u,int v,int c,int f)
    :from(u),to(v),cap(c),flow(f){}
    bool operator<(const Edge& a)const{
        return from<a.from||(from==a.from&&to<a.to);
    }
};
struct Dinic{
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];
    void init(int n){
        this->n=n;
        for(int i=0;i<n;i++)G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap){
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BFS(){
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        queue<int>q;
        q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge& e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a){
        if(x==t||a==0)return a;
        int flow=0,f;
        for(int &i=cur[x];i<G[x].size();i++){
            Edge& e=edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)break;
            }
        }
        return flow;
    }
    int Maxflow(int s,int t){
        this->s=s;this->t=t;
        int flow=0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            flow+=DFS(s,inf);
        }
        return flow;
    }
}g;
const int maxt=25+5;
int n,w[maxt],d[maxt],a[maxt][maxt];
inline int ID(int u,int v){return u*n+v+1;}
inline int ID(int u){return n*n+u+1;}
bool canWin(int team){
    int total=w[team];
    for(int i=0;i<n;i++)total+=a[team][i];
    for(int i=0;i<n;i++)
        if(w[i]>total)return false;
    g.init(n*n+n+2);
    int full=0;
    int s=0,t=n*n+n+1;
    for(int u=0;u<n;u++){
        for(int v=u+1;v<n;v++){
            if(a[u][v]>0)g.AddEdge(s,ID(u,v),a[u][v]);
            full+=a[u][v];
            g.AddEdge(ID(u,v),ID(u),inf);
            g.AddEdge(ID(u,v),ID(v),inf);
        }
        if(w[u]<total)g.AddEdge(ID(u),t,total-w[u]);
    }
    return g.Maxflow(s,t)==full;
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=0;i<n;i++)cin>>w[i]>>d[i];
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)cin>>a[i][j];
        bool first=true;
        for(int i=0;i<n;i++)
        if(canWin(i)){
            if(first)first=false;else cout<<" ";
            cout<<i+1;
        }
        cout<<endl;
    }
    return 0;
}

C.UVA - 10779 (構圖最大流)

題意

Bob與他的朋友交換貼紙;他的這些朋友只交換自己沒有的貼紙;且用的是自己所有的重復貼紙;現在要求Bob最大能得到多少張貼紙; (Bob可以不只用重復的貼紙)

思路

把人和物品都進行編號,添加原點s和匯點e,s到每個物品連邊容量為Bob擁有的數目;所有物品向匯點e連邊容量為1;

如果一個人向他擁有的物品連邊,容量為數目減1,表示他自己會留一個;如果他不擁有某件物品,則物品向這個人連一條邊,表示這個人最多接受一件這個物品;

然後跑一遍最大流就是答案了;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=700+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
struct Edge{
    int from,to,cap,flow;
    Edge(int u,int v,int c,int f)
    :from(u),to(v),cap(c),flow(f){}
    bool operator<(const Edge& a)const{
        return from<a.from||(from==a.from&&to<a.to);
    }
};
struct Dinic{
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];
    void init(int n){
        this->n=n;
        for(int i=0;i<n;i++)G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap){
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BFS(){
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        queue<int>q;
        q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge& e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a){
        if(x==t||a==0)return a;
        int flow=0,f;
        for(int &i=cur[x];i<G[x].size();i++){
            Edge& e=edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)break;
            }
        }
        return flow;
    }
    int Maxflow(int s,int t){
        this->s=s;this->t=t;
        int flow=0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            flow+=DFS(s,inf);
        }
        return flow;
    }
}g;
const int maxt=30+5;
int n,w[maxt],d[maxt],a[maxt][maxt],m;
inline int ID(int u){return u;}///物品
inline int PID(int u){return m+u;}
int S,T;
void build(){
    for(int i=1;i<=m;i++){
        if(a[1][i])
            g.AddEdge(S,i,a[1][i]);
    }
    for(int i=2;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]>=2){
                g.AddEdge(PID(i),ID(j),a[i][j]-1);
            }
            else if(!a[i][j]){
                g.AddEdge(ID(j),PID(i),1);
            }
        }
    }
    for(int i=1;i<=m;i++){
        g.AddEdge(ID(i),T,1);
    }
}
int main()
{
    int t;
    int cast=1;
    cin>>t;
    while(t--){
        cin>>n>>m;
        g.init(n+m+2);
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++){
            int tot;cin>>tot;
            while(tot--){
                int aa;
                cin>>aa;
                a[i][aa]++;
            }
        }
        S=0,T=n+m+1;
        build();
        cout<<"Case #"<<cast++<<": ";
        cout<<g.Maxflow(S,T)<<endl;
    }
    return 0;
}

D.UVA - 11613 (最大費用流)

題意

A公司生產一種元素,給出該元素在未來M個月中每個月的單位售價,最大生產量,生產成本,最大銷售量和最大存儲時間,和每月存儲代價,問這家公司在M個月內所能賺大的最大利潤

思路

建邊的時候,費用我用的是相反數,所以得到最小費用後要去相反數
MCMF的時候,用一個數組紀錄了到達匯點時所花費的最小價值,因為取的是相反數,所以當價值為正時,就表示已經虧本了,所以可以退出了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=700+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
struct Edge{
    int from,to,cap,flow,cost;
    Edge(int u,int v,int c,int f,int w)
    :from(u),to(v),cap(c),flow(f),cost(w){}
};
struct MCMF{
    int n,m;
    vector<Edge>edges;
    vector<int>G[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];
    void init(int n){
        this->n=n;
        for(int i=0;i<n;i++)G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap,int cost){
        edges.emplace_back(from,to,cap,0,cost);
        edges.emplace_back(to,from,0,0,-cost);
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BellmanFord(int s,int t,int& flow,ll& cost){
        for(int i=0;i<n;i++)d[i]=inf;
        memset(inq,0,sizeof(inq));
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=inf;
        queue<int>q;
        q.push(s);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            inq[u]=0;
            for(int i=0;i<G[u].size();i++){
                Edge& e=edges[G[u][i]];
                if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to]){
                        q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]>0)return false;
        flow+=a[t];
        cost+=(ll)d[t]*(ll)a[t];
        for(int u=t;u!=s;u=edges[p[u]].from){
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
        }
        return true;
    }
    int MincostMaxflow(int s,int t,ll& cost){
        int flow=0;
        cost=0;
        while(BellmanFord(s,t,flow,cost));
        return flow;
    }
}g;

int main()
{
    int t;
    int cast=1;
    cin>>t;
    int month,st_cost;
    while(t--){
        cin>>month>>st_cost;
        g.init(month*2+2);
        int source=0,sink=2*month+1;
        for(int i=1;i<=month;i++){
            int make_cost,make_limit,price,sell_limit,max_store;
            cin>>make_cost>>make_limit>>price>>sell_limit>>max_store;
            g.AddEdge(source,i,make_limit,make_cost);
            g.AddEdge(month+i,sink,sell_limit,-price);
            for(int j=0;j<=max_store;j++){
                if(i+j<=month)
                    g.AddEdge(i,month+i+j,inf,st_cost*j);
            }
        }
        ll cost=0;g.MincostMaxflow(source,sink,cost);
        cout<<"Case "<<cast++<<": "<<-cost<<endl;
    }
    return 0;
}

訓練指南 網絡流題集