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;

}

 

你可能感兴趣的:(struct)