线性规划与网络流24题 02太空飞行计划问题 最小割

线性规划与网络流24题 02太空飞行计划问题

题意:

给一个图,一些点带正权值,一些点带负权值。选一个正权值点必须要选一些指定的负权值点,问怎样选点获得最大权值。

思路:

最小割是有多解的,所以只要最大流对了就不管了。。。

这是一个求最大闭合子图的问题。

定理:从源点S向所有正权值点引边,容量为权值;从所有负权点像T引边,容量为权值的绝对值。正权值和负权值点之间有关系则引边,容量为无穷大。跑一遍最大流,ans = (正权值和) - MAX_flow。子图为s-t割中s部分。

割点集具体实现方法就是跑完最大流后s仍然能够到达的点,割点集不唯一。

源码:

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <cmath>

#include <algorithm>

#include <iostream>

#include <queue>

using namespace std;

#define inf (1000000000)

const int MAXN = 1000 + 5;

int head[MAXN], cnt;

char str[10000];

int TOT;

struct Edge

{

    int u, v;

    int ne, flow;

    Edge(){}

    Edge(int _u, int _v, int _flow)

    {

        u = _u, v = _v, flow = _flow;

        ne = head[u];

    }

}edge[MAXN * MAXN * 2];

void add_edge(int u, int v, int flow)

{

    edge[cnt] = Edge(u, v, flow);

    head[u] = cnt++;

    edge[cnt] = Edge(v, u, 0);

    head[v] = cnt++;

}

int n, m;

void init()

{

    TOT = 0;

    cnt = 0;

    memset(head, -1, sizeof(head));

    getchar();

    for(int i = 1 ; i <= n ; i++){

        gets(str);

        int f = 1;

        int temp = 0;

        int j = 0;

//        printf("i = %d\n", i);

        while(str[j] == ' ')    j++;

        for(; str[j] != '\0' ; j++){

//            printf("str[%d] = %c, temp = %d\n", j, str[j], temp);

            if(str[j] >= '0' && str[j] <= '9'){

//                printf("second\n");

                temp = temp * 10 + str[j] - '0';

            }

            else{

                if(f){

//                    printf("%d temp = %d\n", f, temp);

                    TOT += temp;

                    add_edge(0, i, temp);

                    temp = 0; f = 0;

                }

                else{

//                    printf("%d temp = %d\n", f, temp);

                    add_edge(i, temp + n, inf);

                    temp = 0;

                }

            }

        }

        if(f){

            TOT += temp;

            add_edge(0, i, temp);

            temp = 0; f = 0;

//            printf("%d temp = %d\n", f, temp);

        }

        else{

            add_edge(i, temp + n, inf);

            temp = 0;

//            printf("%d temp = %d\n", f, temp);

        }

//        printf("\n");

    }

    int temp = 0;

    for(int i = n + 1 ; i <= n + m ; i++){

        scanf("%d", &temp);

        add_edge(i, n + m + 1, temp);

    }

}

int d[MAXN], vis[MAXN], cur[MAXN];

queue<int>que;

void BFS(int t, int flag)

{

    memset(vis, 0, sizeof(vis));

    memset(d, -1, sizeof(d));

    while(!que.empty()) que.pop();

    d[t] = 0;

    vis[t] = 1;

    que.push(t);

    while(!que.empty()){

        int u = que.front();    que.pop();

        for(int now = head[u] ; now != -1 ; now = edge[now].ne){

            int v = edge[now].v;

            if(vis[v] == 0){

                if(!flag || (flag && edge[now].flow)){

                    d[v] = d[u] + 1;

                    vis[v] = 1;

                    que.push(v);

                }

                if(flag && v == t)

                    return;

            }

        }

    }

}

int p[MAXN], num[MAXN];

int Augment(int s, int t)

{

    int flow = inf;

    int now = t;

    while(now != s){

        flow = min(flow, edge[p[now]].flow);

        now = edge[p[now]].u;

    }

    now = t;

    while(now != s){

        edge[p[now]].flow -= flow;

        edge[p[now] ^ 1].flow += flow;

        now = edge[p[now]].u;

    }

    return flow;

}

int ISAP(int s, int t)

{

    int flow = 0;

    BFS(t, 0);

    memset(num, 0, sizeof(num));

    for(int i = 0 ; i <= n + m + 1 ; i++)

        cur[i] = head[i], num[d[i]]++;

    int u = s;

    while(d[s] < n + m + 3){

        if(u == t){

            flow += Augment(s, t);

            u = s;

        }

        int ok = 0;

        for(int now = cur[u] ; now != -1 ; now = edge[now].ne){

            int v = edge[now].v;

            if(edge[now].flow > 0 && d[v] == d[u] - 1){

                cur[u] = now;

                p[v] = now;

                u = v;

                ok = 1;

                break;

            }

        }

        if(!ok){

            int tm = n + m + 2;

            for(int now = head[u] ; now != -1 ; now = edge[now].ne){

                int v = edge[now].v;

                if(edge[now].flow)  tm = min(tm, d[v]);

            }

            if(--num[d[u]] == 0) break;

            num[d[u] = tm + 1]++;

            cur[u] = head[u];

            if(u != s)  u = edge[p[u]].u;

        }

    }

    return flow;

}

int in[MAXN];

void min_craft()

{

    memset(in, 0, sizeof(in));

    in[0] = 1;

    while(!que.empty()) que.pop();

    que.push(0);

    while(!que.empty()){

        int u = que.front();    que.pop();

        for(int now = head[u] ; now != -1 ; now = edge[now].ne){

            int v = edge[now].v;

            if(edge[now].flow > 0 && !in[v]){

                in[v] = 1;

                que.push(v);

            }

        }

    }

}

int main()

{

    freopen("shut10.in", "r", stdin);

    while(scanf("%d%d", &n, &m) != EOF){

        init();

        int ans = TOT - ISAP(0, n + m + 1);

        BFS(0, 1);

        min_craft();

        int f = 1;

        for(int i = 1 ; i <= n ; i++){

            if(in[i]){

                if(f)   f = 0;

                else    printf(" ");

                printf("%d", i);

            }

        }

        printf("\n");

        f = 1;

        for(int i = n + 1 ; i <= n + m ; i++){

            if(in[i]){

                if(f)   f = 0;

                else    printf(" ");

                printf("%d", i - n);

            }

        }

        printf("\n");

        printf("%d\n", ans);

    }

    return 0;

}

/*

1 7 9 24

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 29 30 31

11

 

Process returned 0 (0x0)   execution time : 0.575 s

Press any key to continue.

 

*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值