现在的位置: 首页 > 综合 > 正文

SGU-176 Flow construction 有上下界的最小流

2012年07月04日 ⁄ 综合 ⁄ 共 5333字 ⁄ 字号 评论关闭

这里参看了大牛的解题思路,学习了很多。原来上下界流的求法是这么的灵活,尤其我是用的临界表存储的边,删除更新很不方便。

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;
}

 

抱歉!评论已关闭.