P2762 太空飞行计划问题【最大权闭合子图 】

题目描述
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集RjÍI。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
输入格式:
第1行有2 个正整数m和n。m是实验数,n是仪器数。接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的n个数是配置每个仪器的费用。
输出格式:
第1 行是实验编号;第2行是仪器编号;最后一行是净收益。
输入样例#1:
2 3
10 1 2
25 2 3
5 6 7
输出样例#1:
1 2
1 2 3
17

分析:
网络流难的不是代码实现,而是思想然后建图,想了好久,最终看了题解,有一个新名词——最大权闭合子图。。。
我个人的一些看法是,最大权闭合子图和一般的二分图多重匹配,区别是每个集合之间内部还会有影响。举个例子,本题中若每个实验用的仪器都不相同,就是个二分图多重匹配了,因为仪器这个集合内部之间有影响,也就是依赖关系,这就是最大权闭合子图问题了。但是二者都是用最小割原理去解的,而且建图和最后求权值的时候竟然也几乎一样,最大权闭合子图=sum-最小割。
建图:
源点->(实验费用)->实验->(INF)->仪器->(仪器费用)->汇点。
割边若是和S相连,则表示这个实验没完成,割边若是和T相连,则表示这个仪器购买了。
其实本来完成实验的费用是得到的,权值是正值,但是仪器购买是花费,建图是应该用的是权值的绝对值(权值为负),所以割边若是和T相连,则表示这个仪器购买了。
ans=sum-实验没完成的退还费用-购买仪器费用。

#include <cstdio>  
#include <cstring>  
using namespace std; 

const int M = 1e5 + 10;
const int N = 1e4 + 10;
const int inf = 1e9; 

int head[N], p, lev[N], cur[N];  
int que[M];

struct node {  
    int u, v, f, next;  
} e[M];

void ainit() {  
    p = 0;
    memset(head, -1, sizeof(head));  
}  

bool bfs(int s, int t) {  
    int i, u, v;
    int qin = 0, qout = 0;  
    memset(lev, -1, sizeof(lev)); 
    lev[s] = 0;
    que[qin++] = s;  
    while(qout != qin) {
        u = que[qout++];
        for(i = head[u]; i != -1; i = e[i].next)
            if(e[i].f > 0 && lev[v = e[i].v] == -1) {
                lev[v] = lev[u] + 1;
                que[qin++] = v;
                if (v == t) return 1;  
            }  
    }  
    return 0;  
}  

int dinic(int s, int t) {  
    int i, f, k, u, qin;  
    int flow = 0;  
    while (bfs(s, t)) {  
        memcpy(cur, head, sizeof (head));  
        u = s, qin = 0;
        while (1) {
            if (u == t) {  
                for (k = 0, f = inf; k < qin; k++)  
                    if (e[que[k]].f < f) f = e[que[i = k]].f;  
                for (k = 0; k < qin; k++)  
                    e[que[k]].f -= f, e[que[k]^1].f += f;  
                flow += f;
                u = e[que[qin = i]].u;  
            }  
            for (i = cur[u]; cur[u] != -1; i = cur[u] = e[cur[u]].next)  
                if (e[i].f > 0 && lev[u] + 1 == lev[e[i].v]) break;  
            if (cur[u] != -1)  {
                que[qin++] = cur[u];
                u = e[cur[u]].v;  
            } 
            else {  
                if (qin == 0) break;  
                lev[u] = -1;
                u = e[que[--qin]].u;  
            }  
        }  
    }  
    return flow;  
}  

void add(int u, int v, int f) {  
    e[p].u = u, e[p].v = v, e[p].f = f, e[p].next = head[u], head[u] = p++;  
    e[p].u = v, e[p].v = u, e[p].f = 0, e[p].next = head[v], head[v] = p++;  
}  

int main() {  
    int n, m, i, p, u, ans, sum;  
    while (scanf("%d%d", &m, &n) != -1) {  
        ainit();  
        for (i = 1, sum = 0; i <= m; i++) {  
            scanf("%d", &p);  
            sum += p, add(0, i, p);  
            while (getchar() != '\n') {  
                scanf("%d", &u);  
                add(i, u + m, inf);  
            }  
        }  
        for (i = 1; i <= n; i++) {  
            scanf("%d", &u);  
            add(i + m, n + m + 1, u);  
        }  
        ans = dinic(0, n + m + 1);  
        for (i = 1; i <= m; i++) if (lev[i] != -1)  
             printf("%d ", i);  
        printf("\n");  
        for (i = m + 1; i <= n + m; i++) if (lev[i] != -1)  
            printf("%d ", i - m);  
        printf("\n%d\n",sum - ans);  
    }  
    return 0;  
}

猜你喜欢

转载自blog.csdn.net/qq_36368339/article/details/80640554