POJ1273
参考:http://www.cnblogs.com/jackge/archive/2013/04/10/3012182.html
裸的最大流题目,要注意重边。
int map[][] ,记录容量
int flow[][]记录流量
int res[]记录残余流量
int pre[]记录父亲节点,更新flow数组是用到
从流量为0开始找增广路,清空flow数组开始。
每次将残余流量数组清空,只有src源点的残余容量是无穷大。
用BFS寻找随机路径,并且同时更新res(残余流量数组)取最小值。
如果能更新到des,也就是 res[des]不等于0 ,则 说明找到了一条增广路,把它加到max_flow里;
然后更新整个网络,也就是flow数组。正流量增加 res[des],反向流量减少res[des];
不断重复上述过程,直到res[des] == 0,找不到增广路,也就是当前已经达到了最大流。(增广路定理)
Edmond-Karp算法
#include <iostream>
#include <queue>
#include <string.h>
#include <cstdio>
#define N 250
#define INF 0x3f3f3f3f
using namespace std;
int map[N][N]; // 容量
int flow[N][N] ; //流量
int res[N];//每条边的残余流量
int pre[N]; //记录父亲节点的数组 //更新汇点的流量之后回溯时用到
int n,m,max_flow;
int EK(int src,int des){
memset(flow,0,sizeof(flow)); // start from zero_flow;
max_flow = 0; //set max_flow to zero
queue<int>Q;
while(1){
memset(res,0,sizeof(res)); // set remains to zero;
res[src] = INF; // start point has infinite flow;
Q.push(src);
while(!Q.empty()){
int u = Q.front(); Q.pop();
for(int v=1;v<=m;v++){
if(!res[v] && map[u][v] > flow[u][v]){
pre[v] = u;
Q.push(v);
res[v] = min(res[u],map[u][v] - flow[u][v]); //如果res[u]不足,就将所有res
//都流入res[v] ,否则 ,将res[v]流满,map[u][v] - flow[u][v];
}
}
}
if(res[des] == 0) return max_flow; //找不到增广路
for(int v=des;v!=src;v=pre[v]){
flow[pre[v]][v] += res[des];
flow[v][pre[v]] -= res[des]; //更新反向弧
}
max_flow += res[des];
}
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
int from,to,w;
memset(map,0,sizeof(map));
for(int i=0;i<n;i++){
scanf("%d%d%d",&from,&to,&w);
map[from][to] += w; //重边
}
int ans = EK(1,m);
printf("%d\n",ans);
}
return 0;
}
POJ1459 多源点,多汇点
EK
处理多个源点可以通过增加一个超级源点,连接到多个源点。
同理增加一个超级汇点,让所有汇点连接到超级汇点。 可以参照算法竞赛入门经典。
#include <iostream>
#include <queue>
#include <string.h>
#include <cstdio>
#define N 110
#define INF 0x3f3f3f3f
using namespace std;
int map[N][N]; // 容量
int flow[N][N] ; //流量
int res[N];//每条边的残余流量
int pre[N]; //记录父亲节点的数组 //更新汇点的流量之后回溯时用到
int n,np,nc,m,max_flow;
int src = 101,des=102;
int EK(int src,int des){
memset(flow,0,sizeof(flow)); // start from zero_flow;
max_flow = 0; //set max_flow to zero
queue<int>Q;
while(1){
memset(res,0,sizeof(res)); // set remains to zero;
res[src] = INF; // start point has infinite flow;
Q.push(src);
while(!Q.empty()){
int u = Q.front(); Q.pop();
for(int v=0;v<=n+1;v++){ // 加入超级源点 汇点 n,n+1
if(!res[v] && map[u][v] > flow[u][v]){
pre[v] = u;
Q.push(v);
res[v] = min(res[u],map[u][v] - flow[u][v]); //如果res[u]不足,就将所有res
//都流入res[v] ,否则 ,将res[v]流满,map[u][v] - flow[u][v];
}
}
}
if(res[des] == 0) return max_flow; //找不到增广路
for(int v=des;v!=src;v=pre[v]){
flow[pre[v]][v] += res[des];
flow[v][pre[v]] -= res[des]; //更新反向弧
}
max_flow += res[des];
}
}
int main(){
while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF){
char s[10];
memset(map,0,sizeof(map));
src = n,des = n+1;
int from,to,w; char ch;
for(int i=0;i<m;i++){
scanf(" %c%d%c%d%c%d",&ch,&from,&ch,&to,&ch,&w);
map[from][to] = w;
}
for(int i=0;i<np;i++){
scanf(" %c%d%c%d",&ch,&from,&ch,&w);
map[src][from] = w; //连接超级源点
}
for(int i=0;i<nc;i++){
scanf(" %c%d%c%d",&ch,&from,&ch,&w);
map[from][des] = w; //连接超级汇点
}
int ans = EK(src,des);
printf("%d\n",ans);
}
return 0;
}
贴一个Dinic的模板
POJ的数据测出来,Dinic比EK还是快了好多。
而之外的两个题的数据反而测出Dinic更慢 - -
【Dinic poj1459】
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#define mem(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define N 111
#define M 111*100
typedef long long ll;
using namespace std;
struct Edge{
int from,to,cap,flow;
};
struct Dinic{
int n,m,s,t; //节点数,边数(包括反向弧),源点,汇点
vector<Edge>edges;
vector<int>G[N];//表示节点i 的第j条边在e数组的编号
bool vis[N]; // bfs中
int d[N];//起点到i的距离
int cur[M]; //当前弧下标
bool BFS(){
mem(vis,0);
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;
}
ll MaxFlow(int s,int t){
this->s = s;this->t = t;
ll flow = 0;
while(BFS()){
mem(cur,0);
flow += DFS(s,INF);
}
return flow;
}
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);
}
void init(){
edges.clear();
for(int i=0;i<=N;i++){
G[i].clear();d[i] =0; vis[i] = 0;
}
mem(cur,0);
}
}solve;
int main(){
int n,np,nc,m;
while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF){
char s[10];
solve.init();
int src = n,des = n+1;
int from,to,w; char ch;
for(int i=0;i<m;i++){
scanf(" %c%d%c%d%c%d",&ch,&from,&ch,&to,&ch,&w);
solve.addEdge(from,to,w);
}
for(int i=0;i<np;i++){
scanf(" %c%d%c%d",&ch,&from,&ch,&w);
solve.addEdge(src,from,w); //连接超级源点
}
for(int i=0;i<nc;i++){
scanf(" %c%d%c%d",&ch,&from,&ch,&w);
solve.addEdge(from,des,w); //连接超级汇点
}
printf("%I64d\n",solve.MaxFlow(src,des));
}
return 0;
}
NYOJ677
做了这个题才知道最大流最小割定理是多么神奇,这个题就是裸求图的最小割,然后我们直接转化为最大流问题处理就行了。
关键在于构图,增添一个超级源点0,将超级源点与间谍所在节点连接,汇点是n。其余的结点相连的权值都设置为1。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
int spy[222];
int map[222][222];
int n,m,q;
int res[222];
int flow[222][222];
int p[222];
int EK(int src,int des){
int max_flow = 0;
memset(flow,0,sizeof(flow));
queue<int>Q;
while(1){
memset(res,0,sizeof(res));
res[src] = INF;
Q.push(src);
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int v = 0;v<=n;v++){
if(!res[v] && map[u][v] > flow[u][v]){
p[v] = u;
res[v] = min(res[u] , map[u][v] - flow[u][v]);
Q.push(v);
}
}
}
// cout<<res[des]<<endl;
if(res[des] == 0) return max_flow;
max_flow += res[des];
for(int v=des;v!=src;v=p[v]){
flow[p[v]][v] += res[des];
flow[v][p[v]] -= res[des];
}
}
}
int main()
{
int T ; scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%d%d%d",&n,&m,&q);
int po;
memset(map,0,sizeof(map));
for(int i=0;i<q;i++){
scanf("%d",&po);
map[0][po] = INF; //超级源点
}
int a,b;
for(int i=0;i<m;i++){
scanf("%d%d",&a,&b);
map[a][b] = map[b][a] = 1;
}
int ans = EK(0,n);
printf("Case #%d: %d\n",cas,ans);
}
return 0;
}
HDU3987
求图的最小割,并且要求割去的边数最少。
通过最小割最大流定理转化成最大流问题。
设置N为大于节点数的一个数。
加权的时候直接将 (新权值) = (权值*N + 1)添入节点中。(注意加的1代表一条边,这样就可以通过取余来算边数了)
这样用原来的方法算出来ans。ans/N 就是最大流, ans%N就是最大流经过的边了。(神构造啊!)
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#define mem(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define N 1110
#define M 111*100
typedef long long ll;
using namespace std;
struct Edge{
int from,to,cap,flow;
};
struct Dinic{
int n,m,s,t; //节点数,边数(包括反向弧),源点,汇点
vector<Edge>edges;
vector<int>G[N];//表示节点i 的第j条边在e数组的编号
bool vis[N]; // bfs中
int d[N];//起点到i的距离
int cur[M]; //当前弧下标
bool BFS(){
mem(vis,0);
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;
}
ll MaxFlow(int s,int t){
this->s = s;this->t = t;
ll flow = 0;
while(BFS()){
mem(cur,0);
flow += DFS(s,INF);
}
return flow;
}
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);
}
void init(){
edges.clear();
for(int i=0;i<=N;i++){
G[i].clear();d[i] =0; vis[i] = 0;
}
mem(cur,0);
}
}solve;
int main(){
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
int n,m;scanf("%d%d",&n,&m);
int u,v,c,d;
solve.init();
for(int i=0;i<m;i++){
scanf("%d%d%d%d",&u,&v,&c,&d);
solve.addEdge(u,v,c * N + 1);
if(d)
solve.addEdge(v,u,c*N+1);
}
ll ans = solve.MaxFlow(0,n-1);
printf("Case %d: %I64d\n",cas,ans%(N));
}
return 0;
}