首先是几份模版
最大流:虽然EK很慢但是优势就是短。求最小割的时候可以根据增广时的a数组来判断哪些边是割边。然而SAP的最大流版我只会套版,并不知道该如何找到这个割边。在尝试的时候发现了一些问题。所以暂且搁下。这个问题目前先就EK把
struct Edge
{
int u,v,next;
LL cap,flow;
}edge[MAXM];
int head[MAXN],tot;
void add_edge(int u,int v,LL cap)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].next = head[u];
edge[tot].flow = 0;
head[u] = tot++;
edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = 0;
edge[tot].flow = 0;
edge[tot].next = head[v];
head[v] = tot++;
}
void init()
{
memset(head,-1,sizeof(head));
tot = 0 ;
}
queue<int>q;
LL a[MAXN];
LL Edmonds_karp(int source,int target)
{
while (!q.empty()) q.pop();
int p[MAXN];
LL F = 0;
while (true)
{
memset(p,-1,sizeof(p));
q.push(source);
memset(a,0,sizeof(a));
a[source] = INF;
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (!a[v] && edge[i].cap > edge[i].flow)
{
a[v] = min(a[u],edge[i].cap - edge[i].flow);
p[v] = i;
q.push(v);
}
}
}
if (a[target] == 0) break;
for (int i = p[target]; i != -1; i = p[edge[i].u])
{
edge[i].flow += a[target];
edge[i ^ 1].flow -= a[target];
}
F += a[target];
}
return F;
}
View Code
接下来是SAP
struct Edge
{
int u,v,next;
int cap,flow;
}edge[MAXM];
int head[MAXN],tot;
int gap[MAXN],dep[MAXN],cur[MAXN];
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void add_edge(int u,int v,int cap,int rcap = 0)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].flow = 0;
edge[tot].next = head[u];
head[u] = tot++;
edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = rcap;
edge[tot].flow = 0;
edge[tot].next = head[v];
head[v] = tot++;
}
int Q[MAXN];
void BFS(int st,int ed)
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
gap[0] = 1;
int front = 0 ,rear = 0;
dep[ed] = 0;
Q[rear++] = ed;
while (front < rear)
{
int u = Q[front++];
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v;
if (dep[v] != -1) continue;
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[MAXN];
int sap(int st,int ed,int N)
{
BFS(st,ed);
memcpy(cur,head,sizeof(head));
int top = 0;
int u = st;
int ans = 0;
while (dep[st] < N)
{
if (u == ed)
{
int Min = INF;
int inser;
for (int i = 0 ; i < top ; i++)
{
if (Min > edge[S[i]].cap - edge[S[i]].flow)
{
Min = edge[S[i]].cap - edge[S[i]].flow;
inser = i;
}
}
for (int i = 0 ; i < top ; i++)
{
edge[S[i]].flow += Min;
edge[S[i] ^ 1].flow -=Min;
}
ans += Min;
top = inser;
u = edge[S[top] ^ 1].v;
continue;
}
bool flag = false;
int v;
for (int i = cur[u] ; i != -1 ; i = edge[i].next)
{
v = edge[i].v;
if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
{
flag = true;
cur[u] = i;
break;
}
}
if(flag)
{
S[top++] = cur[u];
u = v;
continue;
}
int Min = N;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
if (edge[i].cap - edge[i].flow && dep[edge[i].v] < Min)
{
Min = dep[edge[i].v];
cur[u] = i;
}
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if (u != st) u = edge[S[--top] ^ 1].v;
}
return ans;
}
View Code
费用流
struct node
{
int u,v,next;
int flow,cap,cost;
}edge[MAXN * MAXN * 4];
int cnt,src,tag;
int C,F;
bool inq[MAXN];int d[MAXN];
int head[MAXN],p[MAXN];
void add(int u,int v,int cap,int cost)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].cap = cap;
edge[cnt].flow = 0;
edge[cnt].cost = cost;
edge[cnt].next = head[u];
head[u] = cnt++;
//反向
edge[cnt].v = u;
edge[cnt].u = v;
edge[cnt].flow = 0;
edge[cnt].cap = 0;
edge[cnt].cost = - cost;
edge[cnt].next = head[v];
head[v] = cnt++;
}
bool SPFA(int s, int t)
{
while (!q.empty()) q.pop();
memset(inq,false,sizeof(inq));
memset(d,0x3f,sizeof(d));
memset(p,-1,sizeof(p));
d[s] = 0;
q.push(s);
inq[s] = true;
while (!q.empty())
{
int u = q.front(); q.pop();
inq[u] = false;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (d[v] > d[u] + edge[i].cost && edge[i].cap > edge[i].flow)
{
d[v] = d[u] + edge[i].cost;
p[v] = i;
if (!inq[v])
{
q.push(v);
inq[v] = true;
}
}
}
}
return d[tag] != INF;
}
void slove()
{
C = F = 0;
while(SPFA(src,tag))
{
int a = INF;
for (int i = p[tag]; i != -1; i = p[edge[i].u])
a = min(a,edge[i].cap - edge[i].flow);
for (int i = p[tag]; i != -1; i = p[edge[i].u])
{
edge[i].flow += a;
edge[i ^ 1].flow -= a;
}
C += d[tag] * a;
F += a;
}
}
View Code
ZKW费用流
struct Edge
{
int u,v,next;
int cap,flow,cost;
};
struct ZKW_mincostmaxflow
{
int head[MAXN],tot;
Edge edge[MAXM];
int cur[MAXN];
int dis[MAXN];
bool vis[MAXN];
int ss,tt,N;//源点、汇点和点的总个数(编号是0~N-1),不需要额外赋值,调用会直接赋值
int min_cost, max_flow;
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}
void add_edge(int u,int v,int cap,int cost)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].cost = cost;
edge[tot].flow = 0;
edge[tot].next = head[u];
head[u] = tot++;
edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = 0;
edge[tot].cost = -cost;
edge[tot].flow = 0;
edge[tot].next = head[v];
head[v] = tot++;
}
int aug(int u,int flow)
{
if(u == tt)return flow;
vis[u] = true;
for(int i = cur[u];i != -1;i = edge[i].next)
{
int v = edge[i].v;
if(edge[i].cap > edge[i].flow && !vis[v] && dis[u] == dis[v] + edge[i].cost)
{
int tmp = aug(v,min(flow,edge[i].cap-edge[i].flow));
edge[i].flow += tmp;
edge[i^1].flow -= tmp;
cur[u] = i;
if(tmp)return tmp;
}
}
return 0;
}
bool modify_label()
{
int d = INF;
for(int u = 0;u < N;u++)
if(vis[u])
for(int i = head[u];i != -1;i = edge[i].next)
{
int v = edge[i].v;
if(edge[i].cap>edge[i].flow && !vis[v])
d = min(d,dis[v]+edge[i].cost-dis[u]);
}
if(d == INF)return false;
for(int i = 0;i < N;i++)
if(vis[i])
{
vis[i] = false;
dis[i] += d;
}
return true;
}
/*
* 直接调用获取最小费用和最大流
* 输入: start-源点,end-汇点,n-点的总个数(编号从0开始)
* 返回值: pair 第一个是最小费用,第二个是最大流
*/
pair<int,int> mincostmaxflow(int start,int end,int n)
{
ss = start, tt = end, N = n;
min_cost = max_flow = 0;
for(int i = 0;i < n;i++)dis[i] = 0;
while(1)
{
for(int i = 0;i < n;i++)cur[i] = head[i];
while(1)
{
for(int i = 0;i < n;i++)vis[i] = false;
int tmp = aug(ss,INF);
if(tmp == 0)break;
max_flow += tmp;
min_cost += tmp*dis[ss];
}
if(!modify_label())break;
}
return make_pair(min_cost,max_flow);
}
}slove;
View Code
下面题解 其中所有题目大意均是复制别人的1!!
POJ 3436 ACM Computer Factory
大概题意,每个机器有P个组件组成,现在给你M个机器的信息,问你最多能组装多少个电脑。
没行第一个参数 能容纳多少台电脑(可以看成网络流中,没条路的容量)
接下来有2P个参数 0 表示不需要 1表示必须有 2可以可有可无第2~p个参数 分别是安装这个电脑前需要的的条件
第p+1个参数到2P个参数表示 安装好后的机器具备那些组件例1测试数据
最大流,建图应该是比较简单的。源点-------不需要任何前驱条件就能生产的点---------一系列点(其中对应生产为全1的)---连汇点。由于还有容量限制。进行拆点即可。叙述不太好。可以直接看代码build函数
#include
View Code
POJ 3281 Dining
题目大意:有F种食物,D种饮料
N头奶牛,只能吃某种食物和饮料(而且只能吃特定的一份)
一种食物被一头牛吃了之后,其余牛就不能吃了
第一行有N,F,D三个整数
接着2-N+1行代表第i头牛,前面两个整数是Fi与Di(食物与饮料的种类数量),接着是食物的种类与饮料的种类
要求输出最多分配能够满足的牛的数量
建图:源点------食物-----牛------饮品-----汇点 其中牛拆点。对于这样的一系列问题应该是拆有需求的点。而不是拆
被需求的点。虽然食物和饮品都有容量但是并不需要拆他门
#include
View Code
POJ 1087 A Plug for UNIX
有两种物品,一个是插座,一个是电器
插座只有一个插孔和一个插头,电器只有一个插头
首先有n种插座,n种插座用字符串表示,这n种插座可以理解为是插在电源上的插座
然后有m个电器,现在电器要充电,电器用字符串表示,每个电器都有自己可以插的插座
(这个插座可以不是那n个插在电源上的插座,可以是其他的插座)
现在有k个信息
s1 s2代表s1插座可以插到s2插座上去,这里类似于将插头转换了一下
这些s1与s2也可以不是那n个插在电源上的插座给出这些个信息问你还有多少个电器没有插座可以用
建图:也是比较容易的 源点----电器----插座(其中所有可以通过转换器转换的插孔之间全部连边记容量为INF)--汇点
其中插座指的是给与的插座,至于字符窜直接上map
#include
View Code
POJ 2195 Going Home
题意:有一个矩阵,某些格有人,某些格有房子,每个人可以上下左右移动,问给每个人进一个房子,所有人需要走的距离之和最小是多少
解:裸的费用流 就不废话了
#include
View Code
POJ 2516 Minimum Cost
题意:有k种物品,m个供应商,n个收购商。每个供应商和收购商都需要一些种类的物品若干。每个供应商与每个收购商之间的对于不同物品的运费是不同的。求满足收购商要求的情况下,最小运费。
分析:这个题就是输入特别恶心,各种奇奇怪怪的姿势。建图一目了然 源点---供应-收购-汇 容量和费用也容易。可直接看代码
#include
View Code
POJ 1459 Power Network
读完题就会做
#include
View Code
HDU 4280 Island Transport
数据较大的模版题,用sap没递归。不会爆栈。
#include
View Code
HDU 4292 Food
与前面一道题几乎一模一样。拆人不解释
#include
View Code
HDU 4289 Control
大致题意:
给出一个又n个点,m条边组成的无向图。给出两个点s,t。对于图中的每个点,去掉这个点都需要一定的花费。求至少多少花费才能使得s和t之间不连通。
首先有最大流==最小割,再者本题实际是求最小割,那么就可以对每个点进行拆点,求出相应的最大流即可
#include
View Code
UVA 10480 Sabotage
这道题的意思要把一个图分成两部分,要把点1和点2分开。隔断每条边都有一个花费,求最小花费的情况下,应该切断那些边。
这题很明显是最小割,也就是最大流。把1当成源点,2当成汇点。
问题是要求最小割应该隔断那条边。
那么就可以利用EK后的a数组来判断割边
#include
View Code
HDU 2732 Leapin' Lizards
题意比较难。
题目是说一个n*m的迷宫中,有每个格子有柱子。柱子高度为0~3,高度为0的柱子是不能站的(高度为0就是没有柱子)
在一些有柱子的格子上有一些蜥蜴,一次最多跳距离d,相邻格子的距离是1,只要跳出迷宫就是安全的。
这个距离是曼哈顿距离(好像是的)。
蜥蜴一次最多跳距离d,但是起跳的地方的柱子高度会减一,一个柱子同一时间只能有一个蜥蜴
要求最少几个不能逃出迷宫。
注意拆点。拆柱子,容量为高度。其他的建图比较明显
#include
View Code
HDU 3338 Kakuro Extension
题意:
原数谜是个很有趣的游戏,如图,每一行或每一列空白称为一个回,每一回都对应着一个整数sum,sum就是这回的和。这些空白格里只能填入1—9这九个数字,且在每一回中不能重复。全黑色的格为空,有数字的格,左下角的表示列的和,右上角的表示行的和,则可以得到下面这个图。
但这道题不是原来的数谜,这题与原数谜相比,少了一点规则,就是,每一回中出现的数字可以重复。给你一个n * m 的图,让你填充一下。
建图:这题很神啊,我看了题解才会的。建图:源点-有行和的点--对应行和所“管辖”的点-----列和---汇点
#include
View Code
HDU 3605 Escape
题意很短的就不说了
注意到直接裸建图会T,然后M只有10,于是可以状压建图。具体看代码
#include
View Code
HDU 3081 Marriage Match II
女生和男生配对,有些女生相互是朋友,每个女生也可以跟她朋友所配对的男生配对
每次配对,每个女生都要跟不同的男生配对。问最多能配对几轮。
二分答案,最大流判断。判断流量是否==mid*N,建图源点向女生建边,男生向汇点建边,容量均为mid,女生跟所有能配对的男生连线,容量为1
#include
View Code
HDU 3416 Marriage Match IV
题意:某个人从A到B,只走最短路,走过的边不再走,其最多能去几次。
处理出最短路边,然后简单最大流建图。至于怎么找最短路边有比较容易的方法看代码
#include
View Code
呼!!终于糊弄完了!!
1、无源汇有上下界最大流
题目链接: sgu194 Reactor Cooling
题目大意:给n个点,及m根pipe,每根pipe用来流躺液体的,单向的,每时每刻每根pipe流进来的物质要等于流出去的物质,要使得m条pipe组成一个循环体,里面流躺物质。并且满足每根pipe一定的流量限制,范围为[Li,Ri].即要满足每时刻流进来的不能超过Ri(最大流问题),同时最小不能低于Li。
解题思路:O(-1)。
建图模型: 以前写的最大流默认的下界为0,而这里的下界却不为0,所以我们要进行再构造让每条边的下界为0,这样做是为了方便处理。对于每根管子有一个上界容量up和一个下界容量low,我们让这根管子的容量下界变为0,上界为up-low。可是这样做了的话流量就不守恒了,为了再次满足流量守恒,即每个节点"入流=出流”,我们增设一个超级源点st和一个超级终点sd。我们开设一个数组du[]来记录每个节点的流量情况。
du[i]=in[i](i节点所有入流下界之和)-out[i](i节点所有出流下界之和)。
当du[i]大于0的时候,st到i连一条流量为du[i]的边。
当du[i]小于0的时候,i到sd连一条流量为-du[i]的边。
最后对(st,sd)求一次最大流即可,当所有附加边全部满流时(即maxflow==所有du[]>0之和),有可行解。
#include
using namespace std;
const int MAXN = 50010;
const int MAXM = 500010;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int cap,flow,idx;
}edge[MAXM];
int head[MAXN],tot;
int gap[MAXN],dep[MAXN],cur[MAXN];
int N,M;
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void add_edge(int u,int v,int cap,int idx,int rcap = 0)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].flow = 0;
edge[tot].next = head[u];
edge[tot].idx = idx;
head[u] = tot++;
edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = rcap;
edge[tot].flow = 0;
edge[tot].next = head[v];
edge[tot].idx = 0;
head[v] = tot++;
}
int Q[MAXN];
void BFS(int st,int ed)
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
gap[0] = 1;
int front = 0 ,rear = 0;
dep[ed] = 0;
Q[rear++] = ed;
while (front < rear)
{
int u = Q[front++];
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v;
if (dep[v] != -1) continue;
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[MAXN];
int sap(int st,int ed,int N)
{
BFS(st,ed);
memcpy(cur,head,sizeof(head));
int top = 0;
int u = st;
int ans = 0;
while (dep[st] < N)
{
if (u == ed)
{
int Min = INF;
int inser;
for (int i = 0 ; i < top ; i++)
{
if (Min > edge[S[i]].cap - edge[S[i]].flow)
{
Min = edge[S[i]].cap - edge[S[i]].flow;
inser = i;
}
}
for (int i = 0 ; i < top ; i++)
{
edge[S[i]].flow += Min;
edge[S[i] ^ 1].flow -=Min;
}
ans += Min;
top = inser;
u = edge[S[top] ^ 1].v;
continue;
}
bool flag = false;
int v;
for (int i = cur[u] ; i != -1 ; i = edge[i].next)
{
v = edge[i].v;
if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
{
flag = true;
cur[u] = i;
break;
}
}
if(flag)
{
S[top++] = cur[u];
u = v;
continue;
}
int Min = N;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
if (edge[i].cap - edge[i].flow && dep[edge[i].v] < Min)
{
Min = dep[edge[i].v];
cur[u] = i;
}
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if (u != st) u = edge[S[--top] ^ 1].v;
}
return ans;
}
int inde[MAXN],value[MAXN];
int ans[MAXN];
int main() {
int N,M;
scanf("%d%d",&N,&M);
init();
memset(inde,0,sizeof(inde));
memset(value,0,sizeof(value));
for (int i = 0 ; i < M ; i++) {
int u,v,l,r;
scanf("%d%d%d%d",&u,&v,&l,&r);
add_edge(u,v,r - l,i + 1);
inde[v] += l;
inde[u] -= l;
value[i + 1] = l;
}
int source = 0,target = N + 1;
for (int i = 1 ; i <= N ; i++) {
if (inde[i] > 0) add_edge(source,i,inde[i],0);
if (inde[i] < 0) add_edge(i,target,-inde[i],0);
}
sap(source,target,N + 5);
bool flag = false;
for (int i = head[source] ; i != -1 ; i = edge[i].next) {
if (edge[i].flow < edge[i].cap) {
flag = true;
break;
}
}
if (flag) puts("NO");
else {
puts("YES");
for (int i = 0 ; i < tot ; i ++) ans[edge[i].idx] = edge[i].flow;
for (int i = 1 ; i <= M ; i++)
printf("%d\n",ans[i] + value[i]);
}
return 0;
}
2、有源汇有上下界的最大流
题目链接:zoj3229 Shoot the Bullet
tip:注意这个题目还没有测试 ZOJ挂了还无法提交
题目大意:一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝最多个C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制 [Li,Ri],对于每个女神n天的拍照总和不能小于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照;否则输出-1。
解题思路:增设一源点st,汇点sd,st到第i天连一条上界为Di下界为0的边,每个女神到汇点连一条下界为Gi上界为oo的边,对于每一天,当天到第i个女孩连一条[Li,Ri]的边。
建图模型:源点s,终点d。超级源点ss,超级终点dd。首先判断是否存在满足所有边上下界的可行流,方法可以转化成无源汇有上下界的可行流问题。怎么转换呢?
增设一条从d到s没有下界容量为无穷的边,那么原图就变成了一个无源汇的循环流图。接下来的事情一样,超级源点ss连i(du[i]>0),i连超级汇点(du[i]<0),
对(ss,dd)进行一次最大流,当maxflow等于所有(du[]>0)之和时,有可行流,否则没有。
当有可行流时,删除超级源点ss和超级终点dd,再对(s,d)进行一次最大流,此时得到的maxflow则为题目的解。为什么呢?因为第一次 maxflow()只是求得所有满足下界的流量,而残留网络(s,d)路上还有许多自由流(没有和超级源点和超级汇点连接的边)没有流满,所有最终得到的 maxflow=(第一次流满下界的流+第二次能流通的自由流)。
#include
using namespace std;
const int MAXN = 3010;
const int MAXM = 500010;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int cap,flow,idx;
}edge[MAXM];
int head[MAXN],tot;
int gap[MAXN],dep[MAXN],cur[MAXN];
int N,M;
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void add_edge(int u,int v,int cap,int idx = 0,int rcap = 0)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].flow = 0;
edge[tot].next = head[u];
edge[tot].idx = idx;
head[u] = tot++;
edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = rcap;
edge[tot].flow = 0;
edge[tot].next = head[v];
edge[tot].idx = 0;
head[v] = tot++;
}
int Q[MAXN];
void BFS(int st,int ed)
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
gap[0] = 1;
int front = 0 ,rear = 0;
dep[ed] = 0;
Q[rear++] = ed;
while (front < rear)
{
int u = Q[front++];
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v;
if (dep[v] != -1) continue;
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[MAXN];
int sap(int st,int ed,int N)
{
BFS(st,ed);
memcpy(cur,head,sizeof(head));
int top = 0;
int u = st;
int ans = 0;
while (dep[st] < N)
{
if (u == ed)
{
int Min = INF;
int inser;
for (int i = 0 ; i < top ; i++)
{
if (Min > edge[S[i]].cap - edge[S[i]].flow)
{
Min = edge[S[i]].cap - edge[S[i]].flow;
inser = i;
}
}
for (int i = 0 ; i < top ; i++)
{
edge[S[i]].flow += Min;
edge[S[i] ^ 1].flow -=Min;
}
ans += Min;
top = inser;
u = edge[S[top] ^ 1].v;
continue;
}
bool flag = false;
int v;
for (int i = cur[u] ; i != -1 ; i = edge[i].next)
{
v = edge[i].v;
if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
{
flag = true;
cur[u] = i;
break;
}
}
if(flag)
{
S[top++] = cur[u];
u = v;
continue;
}
int Min = N;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
if (edge[i].cap - edge[i].flow && dep[edge[i].v] < Min)
{
Min = dep[edge[i].v];
cur[u] = i;
}
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if (u != st) u = edge[S[--top] ^ 1].v;
}
return ans;
}
int inde[MAXN],value[MAXN];
int id[510][MAXN],res[510][MAXN];
int main() {
while (scanf("%d%d",&N,&M) != EOF) {
init();
int source = 0,target = N + M + 3;
int src = 1,tag = N + M + 2;
memset(inde,0,sizeof(inde));
memset(value,0,sizeof(value));
memset(id,-1,sizeof(id));
memset(res,0,sizeof(res));
for (int i = 1 ; i <= M ; i++) {
int g;
scanf("%d",&g);
inde[N + 1 + i] -= g;
inde[tag] += g;
add_edge(N + 1 + i,tag,INF - g);
}
for (int i = 1 ; i <= N ; i++) {
int C,D;
scanf("%d%d",&C,&D);
add_edge(src,i + 1,D);
for (int j = 1 ; j <= C ; j++) {
int g,l,r;
scanf("%d%d%d",&g,&l,&r);
inde[i + 1] -= l;
g++;
inde[N + 1 + g] += l;
res[i][g] = l;
id[i][g] = tot;
add_edge(i + 1,N + 1 + g,r - l);
}
}
add_edge(tag,src,INF);
int sum = 0;
for (int i = 1 ; i <= tag ; i++) {
if (inde[i] > 0) { add_edge(source,i,inde[i]); sum += inde[i]; }
if (inde[i] < 0) add_edge(i,target,-inde[i]);
}
int ret = sap(source,target,target + 5);
if (ret < sum) puts("-1");
else {
head[source] = -1;
head[target] = -1;
int ans = sap(src,tag,target + 5);
printf("%d\n",ans);
for (int i = 1 ; i <= N ; i++) {
for (int j = 1 ; j <= M ; j++) {
if (id[i][j] == -1) continue;
int index = id[i][j];
printf("%d\n",edge[index].flow + res[i][j]);
}
}
}
}
return 0;
}
3、有源汇有上下界的最小流
题目链接: sgu176 Flow construction
题目大意:有一个类似于工业加工生产的机器,起点为1终点为n,中间生产环节有货物加工数量限制,输出u v z c, 当c等于1时表示这个加工的环节必须对纽带上的货物全部加工(即上下界都为z),c等于0表示加工没有上界限制,下界为0,求节点1(起点)最少需要投放 多少货物才能传送带正常工作。
解题思路:
1、du[i]表示i节点的入流之和与出流之和的差。
2、增设超级源点st和超级汇点sd,连(st,du[i](为正)),(-du[i](为负),sd)。 ///增设超级源点和超级汇点,因为网络中规定不能有弧指向st,也不能有流量流出sd
3、做一次maxflow()。
4、源点(Sd)和起点(St)连一条容量为oo的边。
5、再做一次maxflow()。
6、当且仅当所有附加弧满载时有可行流,最后答案为flow[(Sd->St)^1],St到Sd最大流就是Sd到St最小流。
建图模型:同样转换成先求无源汇有上下界的可行流,先添加一条d到s容量为无穷的边,这里求最小流很容易让人产生歧路,为什么呢?当所有边满足下界条件并且能量守恒时,这时候求得的最大流不就是最小流么。这样是错误了,我开始了在这揣测了良久。
下面来看个例子:
这样求得的最小流为200,而实际的可行最小流解只需100。
问题出在原图中存在环(循环流),而我们没有利用,导致流增大了。
解决方法:先不增加d->s容量为无穷的边,进行一次maxflow(),如果还没有满流,则加一条(d,s)容量为无穷的边,再进行一次maxflow(),当且仅当所有附加弧满载时,有可行解,解为flow[(d->s)^1](即d到s的后悔边权值)。
#include
using namespace std;
const int MAXN = 110;
const int MAXM = 50000;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int cap,flow,idx;
}edge[MAXM];
int head[MAXN],tot;
int gap[MAXN],dep[MAXN],cur[MAXN];
int N,M;
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void add_edge(int u,int v,int cap,int idx = 0,int rcap = 0)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].flow = 0;
edge[tot].next = head[u];
edge[tot].idx = idx;
head[u] = tot++;
edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = rcap;
edge[tot].flow = 0;
edge[tot].next = head[v];
edge[tot].idx = 0;
head[v] = tot++;
}
int Q[MAXN];
void BFS(int st,int ed)
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
gap[0] = 1;
int front = 0 ,rear = 0;
dep[ed] = 0;
Q[rear++] = ed;
while (front < rear)
{
int u = Q[front++];
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v;
if (dep[v] != -1) continue;
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[MAXN];
int sap(int st,int ed,int N)
{
BFS(st,ed);
memcpy(cur,head,sizeof(head));
int top = 0;
int u = st;
int ans = 0;
while (dep[st] < N)
{
if (u == ed)
{
int Min = INF;
int inser;
for (int i = 0 ; i < top ; i++)
{
if (Min > edge[S[i]].cap - edge[S[i]].flow)
{
Min = edge[S[i]].cap - edge[S[i]].flow;
inser = i;
}
}
for (int i = 0 ; i < top ; i++)
{
edge[S[i]].flow += Min;
edge[S[i] ^ 1].flow -=Min;
}
ans += Min;
top = inser;
u = edge[S[top] ^ 1].v;
continue;
}
bool flag = false;
int v;
for (int i = cur[u] ; i != -1 ; i = edge[i].next)
{
v = edge[i].v;
if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
{
flag = true;
cur[u] = i;
break;
}
}
if(flag)
{
S[top++] = cur[u];
u = v;
continue;
}
int Min = N;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
if (edge[i].cap - edge[i].flow && dep[edge[i].v] < Min)
{
Min = dep[edge[i].v];
cur[u] = i;
}
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if (u != st) u = edge[S[--top] ^ 1].v;
}
return ans;
}
int inde[MAXN],value[MAXN];
int ans[MAXM];
int main() {
while (scanf("%d%d",&N,&M) != EOF) {
init();
int source = 0,target = N + 1;
int src = 1,tag = N;
memset(inde,0,sizeof(inde));
for (int i = 1 ; i <= M ; i++) {
int u,v,z,c;
scanf("%d%d%d%d",&u,&v,&z,&c);
if (c == 1) {
inde[u] -= z;
inde[v] += z;
ans[i] = z;
}
else add_edge(u,v,z,i);
}
for (int i = 1 ; i <= N ; i++) {
if (inde[i] > 0) add_edge(source,i,inde[i]);
if (inde[i] < 0) add_edge(i,target,-inde[i]);
}
sap(source,target,target + 1);
add_edge(tag,src,INF);
sap(source,target,target + 1);
bool flag = false;
for (int i = head[source] ; i != -1 ; i = edge[i].next) {
if (edge[i].flow < edge[i].cap) {
flag = true;
break;
}
}
if (flag) puts("Impossible");
else {
int ret = 0,pos = -1;
for (int i = head[tag] ; i != -1 ; i = edge[i].next) {
if (edge[i].v == src) {
pos = i;
break;
}
}
printf("%d\n",edge[pos].flow);
for (int i = 0 ; i < tot ; i++) {
if (edge[i].idx == 0) continue;
ans[edge[i].idx] = edge[i].flow;
}
for (int i = 1 ; i <= M ; i++) printf("%d%c",ans[i] , i == M ? '\n' : ' ');
}
}
return 0;
}
下届最小流问题注意建图即可
#include
using namespace std;
const int MAXN = 20030;
const int MAXD = 110;
const int MAXM = 1000000;
const int INF = 10000000;
struct Edge
{
int u,v,next;
int cap,flow,idx;
}edge[MAXM];
int head[MAXN],tot;
int gap[MAXN],dep[MAXN],cur[MAXN];
int N,M;
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void add_edge(int u,int v,int cap,int idx = 0,int rcap = 0)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].flow = 0;
edge[tot].next = head[u];
edge[tot].idx = idx;
head[u] = tot++;
edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = rcap;
edge[tot].flow = 0;
edge[tot].next = head[v];
edge[tot].idx = 0;
head[v] = tot++;
}
int Q[MAXN];
void BFS(int st,int ed)
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
gap[0] = 1;
int front = 0 ,rear = 0;
dep[ed] = 0;
Q[rear++] = ed;
while (front < rear)
{
int u = Q[front++];
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v;
if (dep[v] != -1) continue;
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[MAXN];
int sap(int st,int ed,int N)
{
BFS(st,ed);
memcpy(cur,head,sizeof(head));
int top = 0;
int u = st;
int ans = 0;
while (dep[st] < N)
{
if (u == ed)
{
int Min = INF;
int inser;
for (int i = 0 ; i < top ; i++)
{
if (Min > edge[S[i]].cap - edge[S[i]].flow)
{
Min = edge[S[i]].cap - edge[S[i]].flow;
inser = i;
}
}
for (int i = 0 ; i < top ; i++)
{
edge[S[i]].flow += Min;
edge[S[i] ^ 1].flow -=Min;
}
ans += Min;
top = inser;
u = edge[S[top] ^ 1].v;
continue;
}
bool flag = false;
int v;
for (int i = cur[u] ; i != -1 ; i = edge[i].next)
{
v = edge[i].v;
if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
{
flag = true;
cur[u] = i;
break;
}
}
if(flag)
{
S[top++] = cur[u];
u = v;
continue;
}
int Min = N;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
if (edge[i].cap - edge[i].flow && dep[edge[i].v] < Min)
{
Min = dep[edge[i].v];
cur[u] = i;
}
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if (u != st) u = edge[S[--top] ^ 1].v;
}
return ans;
}
int mat[MAXD][MAXD];
char str[MAXD];
int id[MAXD][MAXD][2];
int inde[MAXN];
const int dx[] = {
0,1};
const int dy[] = {
1,0};
int main() {
scanf("%d%d",&N,&M);
memset(inde,0,sizeof(inde));
for (int i = 1 ; i <= N ; i++) {
scanf("%s",str + 1);
for (int j = 1 ; j <= M ; j++)
mat[i][j] = str[j] - '0';
}
init();
int source = 0,target = N * M * 2 + 10;
int src = 1,tag = N * M * 2 + 9;
int idx = 1;
memset(id,-1,sizeof(id));
for (int i = 1 ; i <= N ; i++) {
for (int j = 1 ; j <= M ; j++) {
id[i][j][0] = ++idx;
id[i][j][1] = ++idx;
}
}
for (int i = 1 ; i <= N ; i++) {
for (int j = 1 ; j <= M ; j++) {
add_edge(id[i][j][0],id[i][j][1],INF - mat[i][j]);
inde[id[i][j][0]] -= mat[i][j];
inde[id[i][j][1]] += mat[i][j];
add_edge(src,id[i][j][0],INF);
add_edge(id[i][j][1],tag,INF);
for (int d = 0 ; d < 2 ; d++) {
int nx = i + dx[d];
int ny = j + dy[d];
if (nx >= 1 && ny >= 1 && nx <= N && ny <= M)
add_edge(id[i][j][1],id[nx][ny][0],INF);
}
}
}
for (int i = 1 ; i <= tag ; i++) {
if (inde[i] > 0) add_edge(source,i,inde[i]);
if (inde[i] < 0) add_edge(i,target,-inde[i]);
}
sap(source,target,target + 1);
add_edge(tag,src,INF);
sap(source,target,target + 1);
int ret = 0,pos = -1;
for (int i = head[tag] ; i != -1 ; i = edge[i].next) {
if (edge[i].v == src) {
pos = i;
break;
}
}
printf("%d\n",edge[pos].flow);
return 0;
}
View Code