SGU-176 Flow construction 有上下界的最小流
这里参看了大牛的解题思路,学习了很多。原来上下界流的求法是这么的灵活,尤其我是用的临界表存储的边,删除更新很不方便。
http://www.shuizilong.com/house/archives/sgu-176-flow-construction/
http://hi.baidu.com/lxc_0601/blog/item/39e4e2ecd3be0b2f62d09f95.html
这里要进行说明的求解一个有上下界的网络流的步骤:
1.首先进行构图,对于那么对流量没有限制的边,我们直接将容量赋值为原始的容量,而对于有流量要求的边,我们将容量减去下界并将其等价与无下界的边。最后就是添加一个附加汇点和一个附加源点,从附加源点连向每个顶点的容量为以该点所有流入的下界流量总和,每个顶点流向附加汇点是该点流出的下界流量总和。
2.我们要添加一条从汇点到源点流量为INF的边,这条边的意义在于,能够使得源点会汇点满足成为流量平衡条件的普通节点。
3.我们在以附加源点和附加汇点求一次最大流,如果所有的到附加汇点的边都满载,那么说明这个网络是存在满足所有下界的可行流的。因为去除了下界容量的图具备这个能力。但是此时的可行流(从汇点流向源点的流量)并不一定是最小流,因为满足情况的可行流是不唯一的。
4.紧接着,我们在原图上从汇点向源点求一次最大流(此时要删除掉那条从汇点到源点的INF的边),此时便是一个缩流的过程,旨在试探图中是否还存在流量去替代汇点到源点的流量。这里计算出来的结果可能比我们已得到的可行流还要大,意思是说从汇点到源点有的是空间,因此也就不必连接那条INF的边了,整个网络的流量可以为0,网络中存在环流。
由于这里免不了会进行删边的操作,因此我们直接找到那条边,把流量赋值为0就可以了。
代码如下:31MS
#include <cstring> #include <cstdio> #include <cstdio> #include <algorithm> #include <queue> #define INF 0x3fffffff #define RE(x) ((x)^1) using namespace std; int N, M, head[150], dis[150], idx, sum; const int vsource = 110, vsink = 111; int source, sink; queue<int>q; struct Point { int in, out; }p[105]; struct Edge { int v, cap, next, rec; }e[20000]; struct { int x, y, b, c; }info[10015]; void insert(int f, int t, int c, int rec) { ++idx; e[idx].v = t, e[idx].cap = c; e[idx].rec = rec; e[idx].next = head[f], head[f] = idx; } void init() { idx = -1; memset(head, 0xff, sizeof (head)); } bool spfa(int u, int sk) { memset(dis, 0xff, sizeof (dis)); dis[u] = 0; q.push(u); while (!q.empty()) { u = q.front(); q.pop(); for (int i = head[u]; i != -1; i = e[i].next) { if (dis[e[i].v] == -1 && e[i].cap > 0) { dis[e[i].v] = dis[u] + 1; q.push(e[i].v); } } } return dis[sk] != -1; } int dfs(int u, int sk, int flow) { if (u == sk) { return flow; } int tf = 0, sf; for (int i = head[u]; i != -1; i = e[i].next) { if (dis[u]+1 == dis[e[i].v] && e[i].cap > 0 && (sf = dfs(e[i].v, sk, min( flow-tf, e[i].cap )))) { e[i].cap -= sf; e[RE(i)].cap += sf; tf += sf; if (tf == flow) { return tf; } } } if (!tf) { dis[u] = -1; } return tf; } int Dinic(int u, int sk, int flow) { int ans = 0; while (spfa(u, sk)) { ans += dfs(u, sk, flow); } return ans; } /* 5 6 1 2 1 0 2 5 1 0 1 4 2 1 4 5 4 0 1 3 3 0 3 4 2 1 4 0 0 2 4 2 2 5 5 1 2 1 0 2 3 2 1 3 4 2 1 4 2 2 1 3 5 1 0 0 0 2 2 2 0 */ int main() { int ans1, ans2, ans; init(); scanf("%d %d", &N, &M); if (N == 0) { puts("Impossible"); return 0; } source = 1, sink = N; for (int i = 1; i <= M; ++i) { scanf("%d %d %d %d", &info[i].x, &info[i].y, &info[i].b, &info[i].c); if (info[i].c) { sum += info[i].b; p[info[i].x].out += info[i].b; p[info[i].y].in += info[i].b; insert(info[i].x, info[i].y, 0, 0); insert(info[i].y, info[i].x, 0, 0); } else { insert(info[i].x, info[i].y, info[i].b, info[i].b); insert(info[i].y, info[i].x, 0, 0); } } // 接下来遍历所有节点,添加附加源点 for (int i = 1; i <= N; ++i) { insert(vsource, i, p[i].in, p[i].in); insert(i, vsource, 0, 0); insert(i, vsink, p[i].out, p[i].out); insert(vsink, i, 0, 0); } insert(sink, source, INF, INF); insert(source, sink, 0, 0); if (Dinic(vsource, vsink, sum) != sum) { puts("Impossible"); return 0; } ans1 = e[RE(head[sink])].cap; // 这里得到的是一个原网络的可行流,并非最小或者最大流 e[head[sink]].cap = 0; e[RE(head[sink])].cap = 0; ans2 = Dinic(sink, source, INF); // 寻找缩流 ans = ans1 - ans2; if (ans < 0) { // 如果愿网络在不加汇点到源点的INF边足以提供多余可行流的流量 // 那么最小流就可以等于零,内部成环 e[head[sink]].cap = 0; e[RE(head[sink])].cap = 0; insert(vsource, source, -ans, -ans); Dinic(vsource, sink, INF); ans = 0; } printf("%d\n", ans); for (int i = 1; i <= M; ++i) { printf(i == 1 ? "%d" : " %d", e[(i-1)*2+1].cap+info[i].b*info[i].c); } puts(""); return 0; }
二分法枚举法:140MS
#include <cstring> #include <cstdio> #include <cstdio> #include <algorithm> #define INF 0x3fffffff #define RE(x) ((x)^1) using namespace std; int N, M, head[150], dis[150], idx, sum; const int vsource = 110, vsink = 111; int source, sink, que[150], front; struct Point { int in, out; }p[105]; struct Edge { int v, cap, next, rec; }e[20000]; struct { int x, y, b, c; }info[10015]; void insert(int f, int t, int c, int rec) { ++idx; e[idx].v = t, e[idx].cap = c; e[idx].rec = rec; e[idx].next = head[f], head[f] = idx; } void init() { idx = -1; memset(head, 0xff, sizeof (head)); } bool spfa(int u, int sk) { memset(dis, 0xff, sizeof (dis)); dis[u] = 0; front = 0; que[front++] = u; while (front) { u = que[--front]; for (int i = head[u]; i != -1; i = e[i].next) { if (dis[e[i].v] == -1 && e[i].cap > 0) { dis[e[i].v] = dis[u] + 1; que[front++] = e[i].v; } } } return dis[sk] != -1; } int dfs(int u, int sk, int flow) { if (u == sk) { return flow; } int tf = 0, sf; for (int i = head[u]; i != -1; i = e[i].next) { if (dis[u]+1 == dis[e[i].v] && e[i].cap > 0 && (sf = dfs(e[i].v, sk, min( flow-tf, e[i].cap )))) { e[i].cap -= sf, e[RE(i)].cap += sf; tf += sf; if (tf == flow) { return tf; } } } if (!tf) { dis[u] = -1; } return tf; } int Dinic(int u, int sk, int flow) { int ans = 0; while (spfa(u, sk)) { ans += dfs(u, sk, flow); } return ans; } int bsearch(int l, int r) { int mid, ans = -1; while (l <= r) { // printf("mid = %d\n", mid); // printf("l=%d, r=%d\n", l, r); mid = (l+r) >> 1; e[head[sink]].cap = mid; e[RE(head[sink])].cap = 0; if (Dinic(vsource, vsink, INF) == sum) { r = mid-1; ans = mid; } else { l = mid+1; } for (int i = 0; i <= idx; ++i) { e[i].cap = e[i].rec; } } if (ans != -1) { e[head[sink]].cap = ans; e[RE(head[sink])].cap = 0; Dinic(vsource, vsink, INF); } return ans; } /* 5 6 1 2 1 0 2 5 1 0 1 4 2 1 4 5 4 0 1 3 3 0 3 4 2 1 5 5 1 2 1 0 2 3 2 1 3 4 2 1 4 2 2 1 3 5 1 0 */ int main() { int ans1, ans2; init(); scanf("%d %d", &N, &M); if (N == 0) { puts("Impossible"); return 0; } source = 1, sink = N; for (int i = 1; i <= M; ++i) { scanf("%d %d %d %d", &info[i].x, &info[i].y, &info[i].b, &info[i].c); if (info[i].c) { p[info[i].x].out += info[i].b; p[info[i].y].in += info[i].b; insert(info[i].x, info[i].y, 0, 0); insert(info[i].y, info[i].x, 0, 0); } else { insert(info[i].x, info[i].y, info[i].b, info[i].b); insert(info[i].y, info[i].x, 0, 0); } } // 接下来遍历所有节点,添加附加源点 for (int i = 1; i <= N; ++i) { sum += p[i].in; insert(vsource, i, p[i].in, p[i].in); insert(i, vsource, 0, 0); insert(i, vsink, p[i].out, p[i].out); insert(vsink, i, 0, 0); } insert(sink, source, INF, INF); insert(source, sink, 0, 0); ans1 = bsearch(0, 10000000); if (ans1 == -1) { puts("Impossible"); return 0; } printf("%d\n", ans1); for (int i = 1; i <= M; ++i) { printf(i == 1 ? "%d" : " %d", e[(i-1)*2+1].cap+info[i].b*info[i].c); } return 0; }