o(v^2*E)

建图时建一条流量为0的反向边,正向边每减去流量f,反向边增加流量f.对于无向图当做两条边。

cap:每条边最大流量

建图后:

调用DINIC():用bfs()为每个节点进行层次编号,在每种层次编号下,用dinic()即dfs找到所有增广路,加到最大流结果。

hdu1532:Drainage Ditches

theme:给定m条边,n个点,1为源点,n为汇点,给定每条边和改变的最大流量,求最大流。

solution:最大流裸题。

#include<iostream>
#include<vector>
#include<cstdio>
#include<queue>
using namespace std;int const SIZE=300;
int const inf=0x3f3f3f3f;
int dis[SIZE];
int cur[SIZE];
int cnt;//边总数
int m,n,N;//N为网络流中总点数
int s,t;struct Edge
{int u,v,cap;Edge(){}  //为声明edge[][]数组而设Edge(int a,int b,int c):u(a),v(b),cap(c){}
}edge[SIZE*SIZE];vector<int>VE[SIZE];void creatG()
{cnt=0;while(m--){int a,b,c;cin>>a>>b>>c;VE[a].push_back(cnt);edge[cnt++]=Edge(a,b,c);VE[b].push_back(cnt);edge[cnt++]=Edge(b,a,0);//反向边流量为0//正向边下标通过异或得到反向边下标}
}bool bfs()
{fill(dis,dis+N+1,inf);queue<int>Q;dis[s]=1;Q.push(s);while(!Q.empty()){int u=Q.front();Q.pop();for(int i=0;i<VE[u].size();++i){Edge &e=edge[VE[u][i]];if(e.cap>0&&dis[e.v]==inf){dis[e.v]=dis[u]+1;Q.push(e.v);}}}return dis[t]<inf;
}int dinic(int u,int maxflow)//DFS
{if(u==t)return maxflow;for(int &i=cur[u];i<VE[u].size();++i){Edge &e=edge[VE[u][i]];if(e.cap>0&&dis[e.v]==dis[u]+1){int flow=dinic(e.v,min(maxflow,e.cap));//DFSif(flow)//考虑反向边与层次不满足的结点返回为0{e.cap-=flow;edge[VE[u][i]^1].cap+=flow;return flow;}}}return 0;
}int DINIC()
{int ans=0;while(bfs()){for(int i=0;i<=N;++i)cur[i]=0;int flow;while(flow=dinic(s,inf))ans+=flow;}return ans;
}int main()
{while(cin>>m>>n){s=1;t=n;N=n;for(int i=1;i<=N;++i)VE[i].clear();creatG();cout<<DINIC()<<endl;}
}

有了模板之后,网络流的题目关键在于能看出来是网络流求解,并能建好图:

poj3281:Dining

theme:n头牛,f中吃的,d中喝的,给出每头牛想吃哪些食物、哪些饮料,牛要选好一个吃的和一个喝的才算分配好,每种食物和饮料只能分配给一头牛,问最多分配好几头牛?1<=n,f,d<=100

solution:因为每头牛要选一个吃的和一个喝的才算分配好,只选其中一种不算,所以建边时分三层:食物位于第一层,牛位于第二层,饮料位于第一层。源点与所有食物建一条流量为2的边、所有饮料和汇点建一条流量为2的边,牛位于第二层按给出的信息分别于食物和饮料建一条流量为1的边。为避免一只牛选多种食物,因在第二层为每只牛再新增一个点建一条流量为1的边:

void creatG()
{cnt=0;for(int i=1;i<=f;++i){VE[s].push_back(cnt);edge[cnt++]=Edge(s,i+1,1);VE[i+1].push_back(cnt);edge[cnt++]=Edge(i+1,s,0);}for(int i=1;i<=n;++i){int fi,di;scanf("%d%d",&fi,&di);while(fi--){int ff;scanf("%d",&ff);VE[ff+1].push_back(cnt);edge[cnt++]=Edge(ff+1,f+i+1,1);VE[f+i+1].push_back(cnt);edge[cnt++]=Edge(f+i+1,ff+1,0);}VE[f+i+1].push_back(cnt);edge[cnt++]=Edge(f+i+1,f+n+i+1,1);VE[f+i+n+1].push_back(cnt);edge[cnt++]=Edge(f+i+n+1,f+i+1,0);while(di--){int dd;scanf("%d",&dd);VE[f+i+n+1].push_back(cnt);edge[cnt++]=Edge(f+i+n+1,f+2*n+dd+1,1);VE[f+2*n+dd+1].push_back(cnt);edge[cnt++]=Edge(f+2*n+dd+1,f+i+n+1,0);}}for(int i=1;i<=d;++i){VE[f+2*n+i+1].push_back(cnt);edge[cnt++]=Edge(f+2*n+i+1,t,1);VE[t].push_back(cnt);edge[cnt++]=Edge(t,f+2*n+i+1,0);}
}

网络流时间复杂度还是挺高的,如果n很大的话可能建图时就超时了,所以要适当缩点。

hdu3605:Escape

theme:n个人,m个房间,给定每个房间能容纳的人数与每个人能去哪几个房间的信息,问能否安排每个人都进房间?1 <= n <= 100000,1 <= m <= 10

solution:n很大,直接建图超时,但是m很小,所以可以将能进相同种类的人看做一个整体用二进制表示,记录下人数,最多2^10种,即将n缩小了,再建图

//建图部分代码:
unordered_map<int,int>mp;
unordered_map<int,int>::iterator it;void creatG()
{cnt=0;int sz=mp.size();int i=1;for(it=mp.begin();it!=mp.end();++it){++i;VE[s].push_back(cnt);edge[cnt++]=Edge(s,i,it->second);VE[i].push_back(cnt);edge[cnt++]=Edge(i,s,0);int x=it->first;for(int j=m;j>=1;--j){if(x%2){VE[i].push_back(cnt);edge[cnt++]=Edge(i,j+sz+1,it->second);VE[j+sz+1].push_back(cnt);edge[cnt++]=Edge(j+sz+1,i,0);}x>>=1;}}for(int i=1;i<=m;++i){int a;scanf("%d",&a);VE[sz+i+1].push_back(cnt);edge[cnt++]=Edge(sz+i+1,t,a);VE[t].push_back(cnt);edge[cnt++]=Edge(t,sz+i+1,0);}
}
int main()
{while(~scanf("%d%d",&n,&m)){s=1;mp.clear();for(int i=1;i<=n;++i){int x=0;for(int j=1;j<=m;++j){int a;scanf("%d",&a);x=(x<<1)+a;}mp[x]++;}int sz=mp.size();t=m+sz+2;N=t;for(int i=1;i<=N;++i)VE[i].clear();creatG();int ans=DINIC();if(ans==n)puts("YES");elseputs("NO");}
}

费用流

要建反向边,反向边流量为0,费用为-cost

SPFA:

poj2135:Farm Tour

theme:给定n个点,m条边,每条边的长度,问从点1到n再从n回到1且除源点与汇点外,其他点只经过一次的最短路径。1 <= N <= 1000,1 <= M <= 10000 ,路径< 35,000.

solution:就算没有点只经过一次,跑最短路删边再跑一遍最短路也是不对的。有点的限制,考虑用网络流。又最终求最短路,所以考虑最小费用最大流。要再回去,所以在源点之后与汇点之前各加一个点,流量为2,费用为0.每个点只能访问一次,所以定义每条边的流量为1,费用为0.

//给定一个无向图,要从1点到N点再返回1点,每条边最多走一次,问最短需要走多远
//题目给出了N个点、M条边, 边的容量均为1
//添加一个源、连接1点、容量为2
//添加一个汇, N点到汇,容量为2
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;typedef int capa_t;
typedef int cost_t;#define SIZE 1010int N,M;//边
struct edge_t{int s;//起点int e;//终点capa_t capa;//容量cost_t cost;//费用edge_t* next;
}Edge[SIZE*40];
int ECnt;//图
edge_t* Ver[SIZE];//建立边
void mkEdge(int a,int b,capa_t capa,cost_t cost){Edge[ECnt].s = a;Edge[ECnt].e = b;Edge[ECnt].capa = capa;Edge[ECnt].cost = cost;Edge[ECnt].next = Ver[a];Ver[a] = Edge + ECnt ++;Edge[ECnt].s = b;Edge[ECnt].e = a;Edge[ECnt].capa = 0;Edge[ECnt].cost = -cost;Edge[ECnt].next = Ver[b];Ver[b] = Edge + ECnt ++;
}inline edge_t* neg(edge_t* p){return Edge + ( ( p - Edge ) ^ 1 );
}cost_t D[SIZE];
bool Flag[SIZE];
edge_t* Pre[SIZE];//边,通过该数组可以还原最短路径
//SPFA算法,使用cost作为权值来衡量最短路径
void spfa(int s){fill(D,D+N+2,INT_MAX);fill(Flag,Flag+N+2,false);fill(Pre,Pre+N+2,(edge_t*)0);D[s] = 0;Flag[s] = true;queue<int> q;q.push(s);while( !q.empty() ){int u = q.front();q.pop();Flag[u] = false;for(edge_t*p=Ver[u];p;p=p->next){if ( p->capa <= 0 ) continue;int v = p->e;cost_t tmp = D[u] + p->cost;if ( tmp < D[v] ){D[v] = tmp;Pre[v] = p;if ( !Flag[v] )q.push(v), Flag[v] = true;}}}
}//最小费用最大流,s为源,t为汇
int mcmf(int s,int t){int miniCost = 0;while(1){spfa(s);//说明找不到增广路if ( INT_MAX == D[t] ) break;//找出该增广路的可行流int flow = INT_MAX;for(edge_t*p=Pre[t];p;p=Pre[p->s])if ( flow > p->capa )flow = p->capa;//更新该网络for(edge_t*p=Pre[t];p;p=Pre[p->s]){p->capa -= flow;neg(p)->capa += flow;}//更新费用miniCost += D[t] * flow;}return miniCost;
}bool read(){if ( EOF == scanf("%d%d",&N,&M) )return false;ECnt = 0;fill(Ver,Ver+N+2,(edge_t*)0);for(int i=0;i<M;++i){int a,b,c;scanf("%d%d%d",&a,&b,&c);mkEdge(a,b,1,c);mkEdge(b,a,1,c);}//建立源到1的边mkEdge(0,1,2,0);//建立N到汇的边mkEdge(N,N+1,2,0);return true;
}int main(){while( read() )printf("%d\n",mcmf(0,N+1));return 0;
}

dijkstra优化费用流:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e4;
const int inf = 0x3f3f3f3f;
struct edge {//网络的边int to, cap, cost, rev;edge() {}edge(int to, int _cap, int _cost, int _rev) :to(to), cap(_cap), cost(_cost), rev(_rev) {}
};
int V, H[maxn + 5], dis[maxn + 5], PreV[maxn + 5], PreE[maxn + 5];
vector<edge> G[maxn + 5];//点void init(int n) {V = n;for (int i = 0; i <= V; ++i)G[i].clear();
}
//建边与反向边
void AddEdge(int from, int to, int cap, int cost) {G[from].push_back(edge(to, cap, cost, G[to].size()));G[to].push_back(edge(from, 0, -cost, G[from].size() - 1));
}
//MCMF
int Min_cost_max_flow(int s, int t, int f, int& flow) {int res = 0; fill(H, H + 1 + V, 0);while (f) {priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;fill(dis, dis + 1 + V, inf);dis[s] = 0; q.push(pair<int, int>(0, s));while (!q.empty()) {pair<int, int> now = q.top(); q.pop();int v = now.second;if (dis[v] < now.first)continue;for (int i = 0; i < G[v].size(); ++i) {edge& e = G[v][i];if (e.cap > 0 && dis[e.to] > dis[v] + e.cost + H[v] - H[e.to]) {dis[e.to] = dis[v] + e.cost + H[v] - H[e.to];PreV[e.to] = v;PreE[e.to] = i;q.push(pair<int, int>(dis[e.to], e.to));}}}if (dis[t] == inf)break;for (int i = 0; i <= V; ++i)H[i] += dis[i];int d = f;for (int v = t; v != s; v = PreV[v])d = min(d, G[PreV[v]][PreE[v]].cap);f -= d; flow += d; res += d*H[t];for (int v = t; v != s; v = PreV[v]) {edge& e = G[PreV[v]][PreE[v]];e.cap -= d;G[v][e.rev].cap += d;}}return res;
}int a[maxn];
int main()
{int t;scanf("%d",&t);while(t--){int n,k;scanf("%d%d",&n,&k);for(register int i=1;i<=n;++i) scanf("%d",&a[i]);int ss=0,s=1,t=2*n+2,tt=2*n+3;//ss为源点,tt为汇点init(tt+1);//清空vectorAddEdge(ss,s,k,0);//建边同时建反向边AddEdge(t,tt,k,0);for(register int i=1;i<=n;++i){AddEdge(s,i+1,1,0);AddEdge(i+1+n,t,1,0);AddEdge(i+1,i+1+n,1,-a[i]);for(register int j=i+1;j<=n;++j){if(a[j]>=a[i]){AddEdge(1+i+n,1+j,1,0);}}}int ans=0;printf("%d\n",-Min_cost_max_flow(ss,tt,inf,ans));}return 0;
}

最大权闭合子图(求最小割)

构图:

设s为源点,t为汇点。

使s连向所有的正权点(非负权点),边权为点权。

使所有非负权点(负权点)连向t,边权为点权的绝对值(正)。

若需要选y才能选x,连一条由x到y的边,边权是∞。

最大点权和=正权点和-最小割。

theme:有n种物品,价值为v,成本为c,且买物品i可能需要先买m中别的类型物品。求能买到的最大价值物品和。

solution:对于每个物品i,如果v-c>=0则建立一条源点到i,权值为v-c的边,否则建立一条i到汇点,权值为c-v的边。最后算出正权点和-最小割,即-最小割。

#include<iostream>
#include<vector>
#include<cstdio>
#include<queue>
using namespace std;int const SIZE=300;
int const inf=0x3f3f3f3f;
int dis[SIZE];
int cur[SIZE];
int cnt=1;
int needv;
int n,N;
int s,t;struct Edge
{int u,v,cap;Edge(){}  //为声明edge[][]数组而设Edge(int a,int b,int c):u(a),v(b),cap(c){}
}edge[SIZE*SIZE];vector<int>VE[SIZE];void creatG()
{cnt=0;needv=0;for(int i=1;i<=n;++i){int v,c,m;scanf("%d%d%d",&v,&c,&m);if(v>c){VE[s].push_back(cnt);edge[cnt++]=Edge(s,i,v-c);VE[i].push_back(cnt);edge[cnt++]=Edge(i,s,0);needv+=v-c;}else{VE[i].push_back(cnt);edge[cnt++]=Edge(i,t,c-v);VE[t].push_back(cnt);edge[cnt++]=Edge(t,i,0);}while(m--){int pre;scanf("%d",&pre);VE[pre].push_back(cnt);edge[cnt++]=Edge(pre,i,inf);VE[i].push_back(cnt);edge[cnt++]=Edge(i,pre,0);}}
}bool bfs()
{fill(dis,dis+N+1,inf);queue<int>Q;dis[s]=1;Q.push(s);while(!Q.empty()){int u=Q.front();Q.pop();for(int i=0;i<VE[u].size();++i){Edge &e=edge[VE[u][i]];if(e.cap>0&&dis[e.v]==inf){dis[e.v]=dis[u]+1;Q.push(e.v);}}}return dis[t]<inf;
}int dinic(int u,int maxflow)//DFS
{if(u==t)return maxflow;for(int &i=cur[u];i<VE[u].size();++i){Edge &e=edge[VE[u][i]];if(e.cap>0&&dis[e.v]==dis[u]+1){int flow=dinic(e.v,min(maxflow,e.cap));//DFSif(flow)//考虑反向边与层次不满足的结点返回为0{e.cap-=flow;edge[VE[u][i]^1].cap+=flow;return flow;}}}return 0;
}int DINIC()
{int ans=0;while(bfs()){for(int i=0;i<=N;++i)cur[i]=0;int flow;while(flow=dinic(s,inf))ans+=flow;}return ans;
}int main()
{cin>>n;s=0;t=n+1;N=n+2;for(int i=0; i<=N; ++i)VE[i].clear();creatG();int mycost=DINIC();cout<<needv-mycost<<endl;
}

Dinic求最大流/最小割相关推荐

  1. 网络流 最大流 最小割 费用流

    [腾讯文档]网络流初步 网络流初步 文章目录 网络流初步 一.网络流简介 1. 网络 2. 流 3. 再次理解网络流 二.常见题型(三种) 三.相关问题对应算法介绍 1.最大流 (1) FF算法 - ...

  2. 【图割】最大流/最小割算法详解(Yuri Boykov and Vladimir Kolmogorov,2004 )

    本博客主要翻译了Yuri Boykov and Vladimir Kolmogorov在2004年发表的改进最大流最小割算法用于计算机视觉的论文:An Experimental Comparison ...

  3. 学习笔记:网络流基础:理解最大流/最小割定理 (蒋炎岩)

    网络流基础:理解最大流/最小割定理 蒋炎岩 课程链接 有向图的基本概念: 问题引入 直观感受反例 引入重要概念: 割的示例 小结 再来一个问题 例子 可以找到一条路径的情况 可以找到两条路径的情况 问 ...

  4. 最大流最小割经典例题_最大流, 最小割问题及算法实现

    本博客采用创作共用版权协议, 要求署名.非商业用途和保持一致. 转载本博客文章必须也遵循署名-非商业用途-保持一致的创作共用协议. 由于博文中包含一些LaTex格式数学公式, 在简书中显示不好, 所以 ...

  5. 网络流-Dinic求最大流(仅做自己复习,写的很不清楚)

    网络流-Dinic求最大流 2021.8.18 原理:网络流-EK求最大流 在Dinic求最大流中,d数组变成了步数的记录,pre数组变成了对其上一条路径的记录. bool bfs() {queue& ...

  6. P2774-方格取数问题【网络流,最大流,最小割】

    正题 链接: https://www.luogu.org/problemnew/show/P2774 题意 在一个n*m的数字矩阵中取数,取得数不能相邻,求能取到的最大价值. 解题思路 最大价值,那么 ...

  7. [ZJOI2009]狼和羊的故事【网络流】【最大流(最小割)】

    >Description 羊狼圈是一个n*m个矩阵格子,这个矩阵的边缘已经装上了篱笆.在羊狼圈中再加入一些篱笆,将羊狼分开.狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非 ...

  8. Cops and Robbers(最大流 最小割)

    https://vjudge.net/problem/Kattis-copsandrobbers 题意: 将某些位置堵上,不能使得B出去,不同字母不同花费,求最小花费. 解析: 使B与外面分离,就是最 ...

  9. P2598-狼和羊的故事【最大流,最小割】

    正题 评测记录: https://www.luogu.org/recordnew/lists?uid=52918&pid=P2598 大意 有n*m的矩阵,里面有羊和狼(也有可能是空),可以在 ...

最新文章

  1. 安装centos6.0 未找到任何驱动器
  2. 硬盘突然提示没有初始化_新硬盘的分区
  3. 作者:褚金翔(1979-),男,中国农业科学院农业环境与可持续发展研究所助理研究员。...
  4. C# sql参数拼接时,防止sql注入
  5. [08001] Could not create connection to database server. Attempted reconnect 3 times. Giving up.解决办法
  6. Windows10本地数据库搭建(MySQL、PostgreSQL)
  7. 中兴F460电信光纤猫超级用户密码
  8. DEFCON 23|利用U盘60秒打开保险柜
  9. Push to branch was rejected
  10. tree是不是动词_tree是什么意思!
  11. ECCV2022细粒度图像检索SEMICON学习记录
  12. 如何给excel的单元格添加斜线并在对角线
  13. node.js毕业设计安卓在线民宿预定app(程序+APP+LW)
  14. 1.第一节课,从头开始学C语言
  15. Java字母笔顺_j的笔顺 j书写顺序是什么
  16. 用 Flutter 实现 PageView 指示器
  17. 魔坊APP项目-25-种植园,植物的状态改动、当果树种植以后在celery的异步任务中调整浇水的状态、客户端通过倒计时判断时间,显示浇水道具
  18. 揭秘维基解密及创始人:朱利安·阿桑奇 [图]
  19. mac -- brew下载非常慢(2020最新方法)
  20. 如何评估用户需求的价值?关键看4方面

热门文章

  1. AI的边界在哪里?科大讯飞“上天入地”
  2. Python实践-咚咚呛讲师Python进阶教程
  3. scilab安装EMD工具箱
  4. 示例:Linux应用程序遍历当前系统的PCI设备
  5. WIFI WPS 种类
  6. 机器人能送药、治病、消毒,最大程度保护医生护士不被传染!
  7. LPR个人房贷利率如何计算?(商贷部分)
  8. perl范围声明our,my,local
  9. ESP32 之 ESP-IDF 教学(十八)—— 组件配置(KConfig)
  10. 【机考】华为OD2022.11.01机考题目思路与代码