训练指南 网络流题集



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;
}

原文地址:https://www.cnblogs.com/luowentao/p/10353862.html

时间: 2024-11-07 07:50:09

训练指南 网络流题集的相关文章

网络流题集

最大流POJ 1273 Drainage DitchesPOJ 1274 The Perfect Stall (二分图匹配)POJ 1698 Alice's Chance(构图)POJ 1459 Power Network(构图)POJ 2112 Optimal Milking (二分)POJ 2455 Secret Milking Machine (二分)POJ 3189 Steady Cow Assignment (枚举)POJ 1637 Sightseeing tour (混合图欧拉回路)

POJ 3368 Frequent values RMQ 训练指南 好题

1 #include<cstdio> 2 #include<cstring> 3 4 const int maxn=1e5+5; 5 const int inf=0x3f3f3f3f; 6 7 inline int max(int x,int y) 8 { 9 return x>y?x:y; 10 } 11 12 int a[maxn]; 13 int left[maxn]; 14 int right[maxn]; 15 int num[maxn]; 16 int dp[ma

算法竞赛入门经典训练指南-做题详细记录(更新中)

第一章 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 using namespace std; 5 typedef long long ll; 6 //吐槽:WA一次.原因是不会输出 Loowater is doomed.由于原代码没有判断骑士是否已经杀了一个头.鉴于题目里提到n>=1.所以一旦有骑士杀了龙,那么就让骑士 b[i]=-1,这样他就相当于被标记过了.本来还想开个b

《训练指南》——6.13

困于时间缘故和考试缠身,笔者在先前关于<训练指南>的而第二章的数学基础的介绍先告一段落,开始对第一章简单的一些算法基础题目进行介绍. Uva11292: 你的王国里有一条n个头的恶龙,你希望雇一些其实把它杀死(即砍掉所有的头).村里有m个其实可以雇佣,一个能力值为x的其实可以砍掉恶龙一个半径不超过x的头,且需要支付x个金币.如何雇佣其实才能砍掉恶龙的所有头,且需要支付的金币最少?注意,一个其实只能看一个头(且只能被雇用一次). 分析:观察到题目给出支付最少的字眼,我们容易将其往动态规划或者贪心

算法竞赛入门经典-训练指南(10881-Piotr&#39;s Ants)

题目大意: 一根长度为L的木棍一堆蚂蚁爬,向左或向右,速度都为1,若两蚂蚁碰撞则同时转头(转身时间忽略不计),问T时间之后每只蚂蚁的位置: 输入:t,(t个样例),每个样例输入 L,T,n,接下来是n行每行两个数据,一个POS(位置),一个dir(方向): 输出:按输入顺序输出每只蚂蚁的最终位置,若处于碰撞状态则输出Turning,掉下去输出"Fell off": 解题思路: 本题类似于<挑战程序设计>的一道水题(POJ -1852  Ants),思路题:不过本题输入并不一

Trie树基本概念和训练指南

接触Trie树是在选拔赛时候遇到一题目,TLE无数次依然无解,赛后发现字符串统计有一利器名曰"字典树",后来花了一段时间去写Trie,算是基本入门了. 本文主要是介绍一些基本概念,以及一些训练题目,提供大家. 什么叫Trie树? Trie树即字典树. 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字

ACM训练二D题

比赛看到这个题目时候,心花怒放啊,朋友这个题和how many tables这个题目一样嘛,并查集,直接就把自己代码交了,后来一看,傻眼了,得输出的是集合中个数最多的数目.后来直接在合并的代码中小小的改动就行了.其实也可以在每次查后把什么孙子,曾孙全变为儿子.再来一个遍历,把不同门派的弟子统计一下,每次选规模大的数就行了. There is a town with N citizens. It is known that some pairs of people are friends. Acc

【UVA11107 训练指南】Life Forms【后缀数组】

题意 输入n(n<=100)个字符串,每个字符串长度<=1000,你的任务是找出一个最长的字符串使得超过一半的字符串都包含这个字符串. 分析 训练指南上后缀数组的一道例题,据说很经典(估计也就是height分组比较常用).但是训练指南上给出的中文题面真滴坑B啊!书上说,连续出现,我懵逼了好久! 我们把这n个字符串连成一个长的字符串S,且中间用不同的未出现的字符相隔开(为什么隔开我们后面说),比如样例一会变为abcdefg1bcdefgh2cdefghi3.这样每一段是一个原字符串.然后问题转换

训练指南 UVA - 10917(最短路Dijkstra + 基础DP)

layout: post title: 训练指南 UVA - 10917(最短路Dijkstra + 基础DP) author: "luowentaoaa" catalog: true mathjax: true tags: - 最短路 - 基础DP - Dijkstra - 图论 - 训练指南 Walk Through the Forest UVA - 10917 题意 Jimmy打算每天沿着一条不同的路走,而且,他只能沿着满足如下条件的道路(A,B):存在一条从B出发回家的路径,比