[题解] [网络流二十四题(二)] 太空飞行计划问题(最小割)

2.太空飞行计划问题

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

分析: 选和不选问题,转化为最小割模型,对于每个实验来说(不考虑其他实验),若是总的收益比总费用要大,那么就选择这个实验,否则不选,而这个题目只是将其整体化了而已
建图: S向每一个实验建一条容量为实验收益的边,实验向相关器材建一条容量为INF的边,器材向T建一条容量为花费的边
答案就是总收益减去最大流,S与实验相连的边若为满流则为选择该实验

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 2100000000
struct node {
    int to;
    int next;
    int flow;
}e[10000];
int head[200];
int cur[200];
int n,m,s,t,tot=1;
int num[200];
int us[200];
int d[200];
int f[200];
char ch;
queue <int> q;
int read() {
    int ans=0,flag=1;
    ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
void addedge(int u,int v,int w) {
    e[++tot].to=v;
    e[tot].flow=w;
    e[tot].next=head[u];
    head[u]=tot;
    e[++tot].to=u;
    e[tot].flow=0;
    e[tot].next=head[v];
    head[v]=tot;
    return ;
}
int dfs(int now,int flow) {
    if(now==t)
        return flow;
    int use=0;
    for(int i=cur[now];i;i=e[i].next) {
        cur[now]=i;
        if(d[e[i].to]+1==d[now] && e[i].flow>0) {
            int temp=dfs(e[i].to,min(e[i].flow,flow-use));
            use+=temp;
            e[i].flow-=temp;
            e[i^1].flow+=temp;
            if(flow==use)
                return use;
        }
    }
    cur[now]=head[now];

    if(!(--f[d[now]]))
        d[s]=n+m+2;
    ++f[++d[now]];
    return use;
}
int main() {
    int ans=0;
    n=read(),m=read();
    s=0;t=n+m+1;
    for(int i=1;i<=n;i++) {
        int w=read(),to;
        ans+=w;
        addedge(0,i,w);
        while(ch!='\n' && ch!='\r') {
            to=read();
            addedge(i,to+n,INF);
            num[i]++;
        }
    }
    for(int i=n+1;i<=n+m;i++) {
        int w=read();
        addedge(i,t,w);
    }
    f[0]=n+m+2;
    while(d[s]<n+m+2)
        ans-=dfs(s,1<<30);
    q.push(0);
    while(!q.empty()) {
        int now=q.front();
        q.pop();
        for(int i=head[now];i;i=e[i].next) {
            if(e[i].flow==0) continue;
            if(!us[e[i].to]) {
                q.push(e[i].to);
                us[e[i].to]=true;
            }
        }
    }
    for(int i=1;i<=n;i++) {
        if(us[i])
            printf("%d ",i);
    }
    printf("\n");
    for(int i=n+1;i<=n+m;i++) {
        if(us[i])
            printf("%d ",i-n);
    }
    printf("\n");
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值