无源汇上下界可行流:
- 每一个边的流量有上界下界限制:
- 每一个点的所有汇入流量等于该点的所有流出流量
提问:是否存在网路流量满足上述的条件?
解决方案:
- 构建超级源点 source 超级汇点 sink
- 计算
,其中 u 表示指向点 id 的所有点,v 表示从点 id出发的所有点
- 如果
,不做任何操作
- 如果
,从source指向该点连接一条权重为
的边
- 如果
,从该点指向sink连接一条权重为
的边
- 原有网络中的所有边的流量改为其上界减去下界
- 然后从source到sink跑一遍Dinic
- 如果source指出去的边可以做到满流,表示存在满足上下界条件的可行流,直白一点就是Dinic跑出的最大流和建边时记录的source指出去的所有流量之和是否相等,相等表示满流,不相等表示不满流,也就是不存在满足上下界条件的可行流。
配题:HDU 4940(题意科幻,抽象成网络流也很科幻)
题意:TOM要去摧毁一个城市间的运输系统,其中城市用点表示,道路用边表示,为有向边。每条边有两个权重,其中 D 表示摧毁这条路需要多少钱,B 表示将这条路建成一个无向边需要的钱数。
- 什么样的城市运输系统?城市运输系统最开始的状态是一个有向图的强连通分量。
- TOM如何去破环?随意选择点集合S和点集合T,将所有从S指向T的边全部摧毁,此时所需要的总花费为 X。
- Enemy如何去保持城市运输系统为强连通呢?上面第二步提出了S、T结合,现在敌人要做的就是将所有从T指向S的边全部摧毁然后在摧毁的基础上建立无向边,此时所需要的总花费为 Y。
- 如果存在集合S、T使得
,那么TOM就会很不开心,所以输出“unhappy”,除此以外输出“happy”。
解题思路:
首先,建立最初始的网络图,每一个边都有两个权重,也就是TOM摧毁需要 D 花费,另一个就是Enemy重建需要 D+B 花费,将花费抽象为流量,所以对于任意一条边其流量要么是 D 要么是 D+B,也就是说,任意一条路的花费一定在范围内,因为TOM要达到的要求是任意的S、T集合,所以每一条边的流量限制都是这个。现在重新理解一下题意,假设现在随意选取了两个集合S、T,那么一定存在一条边从 S 指向 T,那么这个边就是TOM需要摧毁的,那么同样,因为这个图是一个强连通图,那么一定至少存在一条从 T 指向 S 的边,那么这个边就是Enemy需要拆了重建成无向图的边,因为只有这样才能够继续保持这个图是一个强连通图,那么如果TOM想要总是不亏,那么就是要时刻保证,拆的边的费用等于拆了重盖的费用,那么如果我时刻保持指向一个城市的所有道路的费用和等于从这个城市出发的所有道路的费用和不就可以了吗,这就是一个可行流,其上下界前面说过了,如果存在这样的有界可行流的话,那么表示TOM的要求是可以满足的,如果不存在可行流,TOM就不能达到自己的目的。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 205;
const int INF = 1e17+7;
int n, m;
struct EDGE
{
int u, v;
int flow;
EDGE(int u, int v, int flow): u(u), v(v), flow(flow){}
};
int source, sink;
int d[maxn];
vector<EDGE>edge;
vector<int>graph[maxn];
int sb_node[maxn], sb_edge[maxn];
int depth[maxn];
void init()
{
memset(d, 0, sizeof(d));
for (int i = 0; i <= n+1; i++)
{
graph[i].clear();
}
edge.clear();
}
void add_edge(int u, int v, int flow)
{
edge.push_back(EDGE(u, v, flow));
edge.push_back(EDGE(v, u, 0));
int cnt = edge.size();
graph[u].push_back(cnt-2);
graph[v].push_back(cnt-1);
}
bool dinic_bfs()
{
queue<int>q;
while (!q.empty()) q.pop();
memset(depth, 0, sizeof(depth));
depth[source] = 1;
q.push(source);
while (!q.empty())
{
int cur_node = q.front();
q.pop();
for (int i = 0; i < graph[cur_node].size(); i++)
{
int id = graph[cur_node][i];
int next_node = edge[id].v;
int flow = edge[id].flow;
if (depth[next_node] == 0 && flow > 0)
{
depth[next_node] = depth[cur_node] + 1;
sb_node[next_node] = cur_node;
sb_edge[next_node] = id;
q.push(next_node);
}
}
}
if (depth[sink]) return true;
else return false;
}
int Min(int x, int y)
{
if (x < y) return x;
else return y;
}
int dinic()
{
int cut_flow;
int res = 0;
while (dinic_bfs())
{
cut_flow = INF;
for (int cur_node = sink; cur_node != source; cur_node = sb_node[cur_node])
{
cut_flow = Min(cut_flow, edge[sb_edge[cur_node]].flow);
}
for (int cur_node = sink; cur_node != source; cur_node = sb_node[cur_node])
{
int id = sb_edge[cur_node];
edge[id].flow -= cut_flow;
edge[id^1].flow += cut_flow;
}
res += cut_flow;
}
return res;
}
int main()
{
int T;
int Case = 1;
cin>> T;
while (T--)
{
int sum_flow = 0;
scanf("%d %d", &n, &m);
init();
for (int i = 1; i <= m; i++)
{
int u, v, D, B;
scanf("%d%d%d%d", &u, &v, &D, &B);
d[v] += D;
d[u] -= D;
add_edge(u, v, B);
}
source = 0;
sink = n + 1;
for (int i = 1; i <= n; i++)
{
if (d[i] > 0)
{
sum_flow += d[i];
add_edge(source, i, d[i]);
}
else if (d[i] < 0)
{
add_edge(i, sink , -d[i]);
}
}
cout<< "Case #"<< Case++<< ": ";
if (sum_flow == dinic())
{
cout<< "happy"<< endl;
}
else
{
cout<< "unhappy"<< endl;
}
}
return 0;
}