太空飞行计划问题[网络流24题之2]

来源:互联网 发布:xp升级win7软件 编辑:程序博客网 时间:2024/05/18 03:45

问题描述:

W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 E= { E1E2Em },和进行这些实验需要使用的全部仪器的集合 I= { I1I2In }。 实验 Ej需要用到的仪器是 I 的子集 RjI 。配置仪器 Ik 的费用为 ck 美元。实验 Ej 的赞助商已同意为该实验结果支付 pj 美元。 W 教授的任务是找出一个有效算法, 确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。


编程任务:

对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。

数据输入:

1 行有 2 个正整数 mnm 是实验数, n 是仪器数。接下来的 m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的 n 个数是配置每个仪器的费用。


结果输出:

1 行是实验编号;
2 行是仪器编号;
最后一行是净收益。


输入示例:

2 3
10 1 2
25 2 3
5 6 7


输出示例:

1 2
1 2 3
17


分析

本题也是一个网络流问题(这不废话,标题就是网络流 24 题),这个题比较麻烦的就是建模(话说基本每个题都要建模),建模过程如下:

1 :构造一个图 N ,顶点有 Ii(1<=i<=n) , Ej(1<=j<=m) 以及一个源 S 和汇 T
2 :从源点出发,向每个实验 Ei 引出一条容量为 Pi 的有向边
3 :从每个仪器 Ij 出发,向汇点引出一条容量为 Ck 的有向边
4 :每个实验分别向所需的仪器引出一条容量为 + 的有向边

然后求图 N 的最大流,最大流即需要花费的钱。为什么这样子就可以了求出需要花费的钱?因为对于这样的 N 网来说,图上有一点从 S 流到 T ,就说明有 1 点资金需要花费,而如果从 S 引出的边全部满流就说明,这实验把所有的资金全部花费了。如果从 S 引出的边中某一条边没有满流,就说明做这个实验是可以赚钱的(因为这个实验的资金没有全部花费)。
依照上面的思路,我们就可以利用 dinic 的分层来知道那些实验能赚钱(就是那些能够被分层的实验),在从实验反推器材


代码:

#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <string>#include <sstream>#include <iostream>using namespace std;const int inf = 0x3f3f3f3f;int ans;int sum;int n,m;int tot = 1;int c[147];int head[147],nxt[1147],to[1147],wei[1147];int text[147][147];bool used[147];string str;stringstream ss;int que[147]; void add(int,int,int);bool bfs();int dinic(int,int);int main(){    freopen("shut.in","r",stdin);    freopen("shut.out","w",stdout);    int a;    scanf("%d %d",&n,&m);    getline(cin,str);    for(int i=1;i<=n;++i){        ss.clear();        getline(cin,str);        ss << str;        ss >> a;        add(1,i+1,a);        sum += a;        while(ss >> a){            text[i][++text[i][0]] = a;            add(i+1,n+1+a,inf);        }    }    for(int i=1;i<=m;++i){        scanf("%d",&a);        add(n+1+i,n+m+2,a);    }    int flag;    while(bfs()){        flag = 1;        while(flag){            flag = dinic(1,inf);            ans += flag;        }    }    for(int i=2;i<=n+1;++i)        if(c[i]){            printf("%d ",i-1);            for(int j=1;j<=text[i-1][0];++j)                used[text[i-1][j]] = true;            }    putchar(10);    for(int i=1;i<=m;++i)        if(used[i])            printf("%d ",i);    putchar(10);    printf("%d",sum-ans);    return 0;}void add(int from,int tp,int value){    ++tot;nxt[tot]=head[from];head[from]=tot;to[tot]=tp;wei[tot]=value;    ++tot;nxt[tot]=head[tp];head[tp]=tot;to[tot]=from;wei[tot]=0;}bool bfs(){    memset(c,0,sizeof c);    c[1] = 1;    int now;    int H=0,T=1;    que[1] = 1;    do{        now = que[++H];        for(int i=head[now];i;i=nxt[i])            if(!c[to[i]] && wei[i]){                c[to[i]] = c[now]+1;                que[++T] = to[i];            }    }while(H < T);    return c[n+m+2];}int dinic(int place,int low){    if(place == n+m+2)        return low;    int lower;    for(int i=head[place];i;i=nxt[i])        if(c[to[i]]==c[place]+1 && wei[i]){            lower = dinic(to[i],min(low,wei[i]));            if(lower){                wei[i] -= lower;                wei[i^1] += lower;                return lower;            }        }    return false;}
0 0