有上下界的网络流专辑
来源:互联网 发布:java cs架构开发 编辑:程序博客网 时间:2024/04/29 18:06
相对于一般的网络流,有上下界的网络流的某些边多出了流量下界的限制,如边u->v,上下界为high、low,如果有流经过这条边,这个流必须在[low,high]这个区间内。这类题目主要要求解决下面三个问题,“有源汇、无源汇的可行流”、“有源汇的最大流”、“有源汇的最小流”,注意这里所说的源汇是原网络中的源汇,分别记为s、t。
这类题目的难点在于下界的限制很难处理,我们将所有有下界限制的边中分离出“必须边”来单独做考虑,所谓“必须边”就是必须要满流的边,如上述的边,我们可以拆成两条边,第一条是“必须边”,流量上限为low,第二条是一般的边,流量上限为high-low。
怎样对必须边进行考虑呢?我们建立新的超级源汇,记为ss、tt,对于所有必须边再次进行拆边处理,如上述必须边u->v,应该拆成ss->v、u->tt两条流量上限都为low的边(这里很重要,应该要好好理解,我当时学的时候是不断画图来体会这种思想的)。
经过上述步骤,我们所需要的网络已经建立好了,依次来分析那三个问题。(这里所提到的所有变量均为上面所定义的)
一、有源汇、无源汇的可行流。
求可行流,其实就是问是否存在一个方案可以使所有必须边都满流。对于有源汇的网络,我们可以添加一条边t->s,流量上限为INF,这样就变成了无源汇的网络。对于无源汇的网络,只要对ss到tt求一次最大流,若所有必须边都满流,则有可行解,若要求打印方案,只需将非必须边中流过的流量加上流量下界(必须边已满流)。
二、有源汇的最大流
这里的最大流,前提是使所有必须边满流,再要求从s到t的流量最大(注意,这里所求的最大流是原网络的最大流,而我们求ss到tt的最大流只是用于判断是否能使所有必须边满流)。首先判断所有必须边是否满流,这里和问题一中提到的方法一样,注意这里是有源汇的网络。然后直接对残留网络求一次从s到t的最大流,这个最大流就是最终答案。
三、有源汇的最小流
和问题二相反,我们要使所有必须边满流的情况下,要求从s到t的流量最小。这个问题比上面的问题都要复杂,分三个步骤。
1、对ss到tt求一次最大流,记为f1。(在有源汇的情况下,先使整个网络趋向必须边尽量满足的情况)
2、添加一条边t->s,流量上限为INF,这条边记为p。(构造无源汇网络)
3、对ss到tt再次求最大流,记为f2。(要判断可行,必须先构造无源汇网络流,因此要再次求最大流)
如果所有必须边都满流,证明存在可行解,原图的最小流为“流经<边p>的流量”(原图已构造成无源汇网络,对于s同样满足入流 == 出流,只有新添加的边流向s,而s的出流就是原图的最小流)。
这类题目的建模难度都很小,几乎可以一眼看出网络流模型,主要是构图、求解方面的问题,这里贴出我找到的仅有的6道题目的核心代码,所有求最大流都是用Dinic,这里为了尽量简洁略去Dinic的实现。然后题意、类型、题解也不写了,认真读完上面的讲解已经足够解决以下问题。
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2314 ZOJ 2314
// 100 Ms#include <iostream>#include <cstring>#include <cstdio>#include <queue>using namespace std;const int INF = 1e9;const int N = 210;const int M = 10*N*N; struct Data{ int x, y, f, next;} edge[M];int inx;int node[N], level[N]; void addedge(int u, int v, int f){ edge[inx].x = u, edge[inx].y = v, edge[inx].f = f; edge[inx].next = node[u], node[u] = inx++; edge[inx].x = v, edge[inx].y = u, edge[inx].f = 0; edge[inx].next = node[v], node[v] = inx++;} int main(){ int Case, n, m; scanf("%d", &Case); while (Case--) { inx = 0; memset(node, -1, sizeof(node)); scanf("%d %d", &n, &m); int ss = 0, tt = n+1; int a, b, c, d; queue< pair<int, int> > que; int sum = 0; while (m--) { scanf("%d %d %d %d", &a, &b, &c, &d); addedge(a, tt, c); addedge(ss, b, c); sum += c; que.push(make_pair(c, inx)); // 记录每条边的下限、非必须边的下标 addedge(a, b, d-c); } if (sum != Dinic(ss, tt)) printf("NO\n"); else { printf("YES\n"); while ( !que.empty() ) { int down = que.front().first; int p = que.front().second; printf("%d\n", down + edge[p^1].f); que.pop(); } } if (Case) printf("\n"); } return 0;}
http://acm.sgu.ru/problem.php?contest=0&problem=242 SGU 242
// 31 Ms#include <iostream>#include <cstring>#include <cstdio>#include <queue>using namespace std;const int INF = 1e9;const int N = 510;const int M = N*N; struct Data{ int x, y, f, next;} edge[M];int inx;int node[N], level[N]; void addedge(int u, int v, int f){ edge[inx].x = u, edge[inx].y = v, edge[inx].f = f; edge[inx].next = node[u], node[u] = inx++; edge[inx].x = v, edge[inx].y = u, edge[inx].f = 0; edge[inx].next = node[v], node[v] = inx++;} int main(){ int n, m; while (scanf("%d %d", &n, &m) != EOF) { inx = 0; memset(node, -1, sizeof(node)); int s = 0, t = n+m+1; int ss = n+m+2, tt = ss+1; int sum = 0; for (int i = 1; i <= n; i++) addedge(s, i, 1); // 每个学生只能去一个学校,所以流量上限为1 for (int i = n+1; i <= n+m; i++) { sum += 2; addedge(i, tt, 2); // 最少要有两个学生才能去第i间学校(下界) addedge(ss, t, 2); addedge(i, t, INF); // 最多能有无数人去这间学校(上界) } int k, tmp; for (int i = 1; i <= n; i++) { scanf("%d", &k); while (k--) { scanf("%d", &tmp); addedge(i, n+tmp, 1); // 学生和想去的学校间连接流量上限为1的边 } } addedge(t, s, INF); if (sum != Dinic(ss, tt)) printf("NO\n"); else { printf("YES\n"); queue<int> que[210]; for (int i = 1; i <= n; i++) { for (int ip = node[i]; ip != -1; ip = edge[ip].next) { Data &v = edge[ip]; if (v.y > n && v.y <= n+m && edge[ip^1].f > 0) { // 判断是否代表学校的节点、判断是否有流经过 que[v.y-n].push(i); break; } } } for (int i = 1; i <= m; i++) { printf("%d", que[i].size()); while ( !que[i].empty() ) { printf(" %d", que[i].front()); que[i].pop(); } printf("\n"); } } } return 0;}
http://acm.hdu.edu.cn/showproblem.php?pid=3157 HDU 3157
// 15 Ms#include <iostream>#include <cstring>#include <cstdio>#include <queue>using namespace std;const int INF = 1e9;const int N = 110;const int M = N*N; struct Data{ int x, y, f, next;} edge[M];int inx;int node[N], level[N]; void addedge(int u, int v, int f){ edge[inx].x = u, edge[inx].y = v, edge[inx].f = f; edge[inx].next = node[u], node[u] = inx++; edge[inx].x = v, edge[inx].y = u, edge[inx].f = 0; edge[inx].next = node[v], node[v] = inx++;} int main(){ int n, m; while (scanf("%d %d", &n, &m) != EOF) { if (n+m == 0) break; inx = 0; memset(node, -1, sizeof(node)); int s = n+1, t = n+2; int ss = n+3, tt = n+4; char a[5], b[5]; int c, sum = 0; while (m--) { scanf("%s %s %d", a, b, &c); int u, v; if (a[0] == '+') u = s; else sscanf(a, "%d", &u); // 注意这里读取信息 if (b[0] == '-') v = t; else sscanf(b, "%d", &v); addedge(u, tt, c); addedge(ss, v, c); sum += c; addedge(u, v, INF); // 非必须边流量上限为无穷 } int f1 = Dinic(ss, tt); // 求最小流 int p = inx; addedge(t, s, INF); int f2 = Dinic(ss, tt); if (f1+f2 != sum) printf("impossible\n"); else printf("%d\n", edge[p^1].f); } return 0;}
http://acm.sgu.ru/problem.php?contest=0&problem=176 SGU 176
// 46 Ms#include <iostream>#include <cstring>#include <cstdio>#include <queue>using namespace std;const int INF = 1e9;const int N = 110; struct Data{ int x, y, f, next;} edge[N*N];int inx;int node[N], level[N]; void addedge(int u, int v, int f){ edge[inx].x = u, edge[inx].y = v, edge[inx].f = f; edge[inx].next = node[u], node[u] = inx++; edge[inx].x = v, edge[inx].y = u, edge[inx].f = 0; edge[inx].next = node[v], node[v] = inx++;} int main(){ int n, m; while (scanf("%d %d", &n, &m) != EOF) { inx = 0; memset(node, -1, sizeof(node)); // 构图 int ss = n+1, tt = n+2; int a, b, c, d, sum = 0; // 记录必须边总流量 queue< pair<int, int> > que; // 记录边的顺序、下标 while (m--) { scanf("%d %d %d %d", &a, &b, &c, &d); if (d == 1) // 必须边 { sum += c; que.push(make_pair(1, c)); addedge(a, tt, c); addedge(ss, b, c); } else { que.push(make_pair(0, inx)); addedge(a, b, c); } } // 核心代码,求有下界的最小流 int flow1 = Dinic(ss, tt); int pos = inx; addedge(n, 1, INF); int flow2 = Dinic(ss, tt); if (flow1+flow2 != sum) { printf("Impossible\n"); continue; } printf("%d\n", edge[pos^1].f); while ( !que.empty() ) { if (que.front().first == 1) printf("%d ", que.front().second); else { int t = que.front().second; printf("%d ", edge[t^1].f); } que.pop(); } printf("\n"); } return 0;}
http://poj.org/problem?id=2396 POJ 2396
// 79 Ms#include <iostream>#include <cstring>#include <cstdio>#include <queue>#include <stack>using namespace std; const int N = 300;const int M = 100000;const int INF = 1e9; int low[N][N], high[N][N];int column[N], row[N]; struct Data{ int x, y, f, next;} edge[M];int inx;int node[N], level[N]; void addedge(int u, int v, int f){ edge[inx].x = u, edge[inx].y = v, edge[inx].f = f; edge[inx].next = node[u], node[u] = inx++; edge[inx].x = v, edge[inx].y = u, edge[inx].f = 0; edge[inx].next = node[v], node[v] = inx++;} void Limit_flow(int n, int m, int ss, int tt, int sum){ int flow = Dinic(ss, tt); // 计算超级源汇间的最大流 if (flow != sum) // 判断最大流是否等于必须边的流总量 printf("IMPOSSIBLE\n"); // 满足则源汇间存在可行流 else { for (int i = 1; i <= n; i++) // 计算可行流的实际方案 { for (int ip = node[i]; ip != -1; ip = edge[ip].next) { Data &v = edge[ip]; if (n+1 <= v.y && v.y <= n+m) // 判断是否列节点 low[i][v.y-n] += edge[ip^1].f; // 必须边的流量 + 额外通过的流量 } } for (int i = 1; i <= n; i++) // 打印可行方案 { for (int j = 1; j <= m; j++) printf(j == 1? "%d":" %d", low[i][j]); printf("\n"); } }} bool Construct(int n, int m, int ss, int tt){ int sum = 0; // 统计必须边的流总量 int s = n+m+1, t = s+1; // 原图的源、汇 addedge(t, s, INF); // 构造无源汇图 for (int i = 1; i <= n; i++) // 从源点向所有行节点引流 { sum += column[i]; addedge(s, tt, column[i]); // 拆边,原来是 s -> i 节点的边 addedge(ss, i, column[i]); // 拆分成 s -> tt,ss -> i 两条边 } for (int j = 1; j <= m; j++) // 从所有列节点向汇点引流 { sum += row[j]; addedge(n+j, tt, row[j]); addedge(ss, t, row[j]); } for (int i = 1; i <= n; i++) // 行节点 -> 列节点 引流 { // 分离必须边(拆边) for (int j = 1; j <= m; j++) { if (low[i][j] > high[i][j]) // 边界流量限制不合法 return 0; sum += low[i][j]; addedge(i, tt, low[i][j]); // 必须边的流量为下界low[i][j] addedge(ss, n+j, low[i][j]); // 分离成 i->tt, ss->n+j addedge(i, n+j, high[i][j]-low[i][j]); } // 将剩余流量在原节点补充 i->n+j } Limit_flow(n, m, ss, tt, sum); return 1;} void Ini(int x, int y, char ch, int weight) // 按要求缩小每条边的上、下界{ if (ch == '=') { low[x][y] = max(low[x][y], weight); high[x][y] = min(high[x][y], weight); } else if (ch == '<') high[x][y] = min(high[x][y], weight-1); else low[x][y] = max(low[x][y], weight+1);} int main(){ int Case, n, m, k; scanf("%d", &Case); while (Case--) { // 初始化邻接表 inx = 0; memset(node, -1, sizeof(node)); scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &column[i]); for (int j = 1; j <= m; j++) scanf("%d", &row[j]); for (int i = 1; i <= n; i++) // 初始化每条边的上下界 { for (int j = 1; j <= m; j++) { low[i][j] = 0; high[i][j] = INF; } } // 读入限制条件 int a, b, c; char op[2]; scanf("%d", &k); while (k--) { scanf("%d %d %s %d", &a, &b, op, &c); if (a == 0 && b == 0) for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) Ini(i, j, op[0], c); else if (a == 0) for (int i = 1; i <= n; i++) Ini(i, b, op[0], c); else if (b == 0) for (int j = 1; j <= m; j++) Ini(a, j, op[0], c); else Ini(a, b, op[0], c); } // 构图,行节点编号为1~n,列节点编号为n+1~n+m,源、汇为n+m+1,n+m+2 int ss = n+m+3, tt = ss+1; // 超级源、汇 if (Construct(n, m, ss, tt) == 0) // 构造原图 printf("IMPOSSIBLE\n"); if (k) printf("\n"); } return 0;}
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3229 ZOJ 3229
// 1790 Ms#include <iostream>#include <cstring>#include <cstdio>#include <queue>using namespace std;const int INF = 1e9;const int N = 2010;const int M = 500*N; // N*N 会MLE,100*N 会RE,500*N 刚好AC struct Data{ int x, y, f, next;} edge[M];int inx;int node[N], level[N]; void addedge(int u, int v, int f){ edge[inx].x = u, edge[inx].y = v, edge[inx].f = f; edge[inx].next = node[u], node[u] = inx++; edge[inx].x = v, edge[inx].y = u, edge[inx].f = 0; edge[inx].next = node[v], node[v] = inx++;} int main(){ int n, m; while (scanf("%d %d", &n, &m) != EOF) { inx = 0; memset(node, -1, sizeof(node)); int s = n+m+10, t = s+1; int ss = t+1, tt = ss+1; int sum = 0; queue< pair<int, int> > que; int a, b, c; for (int i = 0; i < m; i++) { scanf("%d", &a); addedge(i, tt, a); addedge(ss, t, a); sum += a; addedge(i, t, INF); // 每个女孩拍照下限为ai,上限为INF } for (int i = 1; i <= n; i++) { int C, D; scanf("%d %d", &C, &D); addedge(s, m+i, D); // 每天最多拍照数为D for (int j = 1; j <= C; j++) { scanf("%d %d %d", &a, &b, &c); addedge(m+i, tt, b); addedge(ss, a, b); sum += b; que.push(make_pair(b, inx)); addedge(m+i, a, c-b); // 该天a女孩拍照下限为b,上限为c } } addedge(t, s, INF); // 求有上下界的最大流 int flow = Dinic(ss, tt); if (sum != flow) printf("-1\n"); else { printf("%d\n", Dinic(s, t)); while ( !que.empty() ) { int down = que.front().first; int p = que.front().second; printf("%d\n", down + edge[p^1].f); que.pop(); } } printf("\n"); } return 0;}
- 有上下界的网络流专辑
- 有上下界的网络流
- 【有上下界的网络流】
- 有上下界限制的网络流
- 有上下界的网络流问题
- 有上下界的网络流
- 有上下界的网络流
- 有上下界的网络流问题
- 有上下界的网络流
- 有上下界的网络流
- 【有上下界的网络流】
- 【自用】有上下界的网络流
- 有上下界的网络流问题
- 有上下界的网络流
- [总结]有上下界的网络流
- [有上下界的网络流]
- 有上下界的网络流
- 有上下界的网络流
- SQL Server 智能感知插件SQL Prompt 4 智能提示
- Comparator和Comparable
- system_v 简单操作
- 关于xcode编译事件shell脚本问题
- 在VB中怎么实现COM+
- 有上下界的网络流专辑
- asp.net页生命周期示图
- hadoop学习笔记之-hbase完全分布模式安装
- EasyBook組件常用設置及小技巧
- webservice(五)有关Soap协议的客户端和服务器端的实例代码(基础与进阶)
- 技术(7)——让函数根据一个以上的对象类型来决定如何虚化
- 移动云计算中选择推广应用程序的战术
- Property's synthesized getter follows Cocoa naming convention for returning 'owned' objects
- 万达感受2012-12-27