[网络流24题][洛谷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

题解

最大权闭合子图
每个实验可看成权值为其收益的点,每个器材可以看成权值为其消费的相反数的点。
然后每个实验向所在器材连边,得到的图即为闭合图。我们需要求的即为其中的最大权闭合子图的权值和。
建立一个超级源点S,超级汇点T
从S向每个实验(权值为正的点)连一条容量为该点权值的边,从每个实验(权值为负的点)到T连一条该点权值的相反数的边
其他原有的边视为正无穷
则可以证明原图最大权闭合子图权值和为(所有正权值的点权之和-该图的最小割)。证明过程可参阅以下链接

最大权闭合子图证明过程http://www.cnblogs.com/wuyiqi/archive/2012/03/12/2391960.html

至于求选哪些器材,由于图较小(n,m<50)则可以枚举与T连接的边
断开检验,选哪些实验可以根据器材确定。
还有就是输入比较麻烦,可以逐行读入+sscanf

My Code

/*************************************************************************
    > File Name: test.cpp
    > Author: infinityedge
    > Mail: 309173017@qq.com 
    > Created Time: 2017年01月31日 星期二 19?22分59秒
 ************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
struct edge{
    int fr,to,nxt,cap,flow;
}w[500005];
int h[1005],cnt=-1,cur[1005];
int n,m,s,t;
int dis[1005],vis[1005];
int v[1005],qc[1005][105],z[1005];
int yy[500005];
inline int addedge(int x,int y,int cp){
    cnt++;
    w[cnt].fr=x;
    w[cnt].to=y;
    w[cnt].nxt=h[x];
    w[cnt].cap=cp;
    w[cnt].flow=0;
    h[x]=cnt;
    cnt++;
    w[cnt].fr=y;
    w[cnt].to=x;
    w[cnt].nxt=h[y];
    w[cnt].cap=0;
    w[cnt].flow=0;
    h[y]=cnt;
}
inline int bfs(){
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=h[x];i!=inf;i=w[i].nxt){
            if(!vis[w[i].to]&&w[i].cap-w[i].flow>0){
                q.push(w[i].to);
                vis[w[i].to]=1;
                dis[w[i].to]=dis[x]+1;
            }
        }
    }

    return vis[t];
}
int dfs(int x,int a){
    if(x==t||a==0)return a;
    int minf,resf=0;
    for(int i=h[x];i!=inf;i=w[i].nxt){
        if(dis[w[i].to]==dis[x]+1){
            minf=dfs(w[i].to,min(a,w[i].cap-w[i].flow));
            if(minf>0){
                w[i].flow+=minf;
                w[i^1].flow-=minf;
                resf+=minf;
                a-=minf;
                if(a==0)break;
            }
        }
    }
    //printf("%d\n",resf);
    return resf;

}
inline int dinic(){
    int max_flow=0;
    while(bfs()){
        for(int i=1;i<=1003;i++)cur[i]=h[i];
            max_flow+=dfs(s,inf);
        //printf("%d\n",max_flow);
    }

    return max_flow;
}
int main(){
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    memset(h,0x3f,sizeof(h));
    scanf("%d%d",&m,&n);
    s=1002,t=1003;
    char c[10005];
    v[0]=0;
    for(int i=1;i<=m;i++){
        scanf("%d",&v[i]);
        addedge(s,i,v[i]);
        v[0]+=v[i];
        memset(c,0,sizeof(c));
        cin.getline(c,10000);
        int num=0,len=0;
        while (sscanf(c+len,"%d",&num)==1){
            //printf("%d ",num);
            qc[i][++qc[i][0]]=num;
            addedge(i,m+num,inf);
            if (num==0) len++;
            else while (num){
                num/=10;
                len++;
            }
            len++;
        }
    }
    int cntl=cnt+1;
    int sy[1005];
    memset(sy,0,sizeof(sy));
    for(int i=1;i<=n;i++){
        scanf("%d",&z[i]);
        addedge(i+m,t,z[i]);
    }
    int cntr=cnt-1;
    for(int i=0;i<=cnt;i++){
        yy[i]=w[i].cap;
    }

    int ans=dinic();
    for(int i=h[t];i!=inf;i=w[i].nxt){
        //printf("%d ",w[i].to-m);
        for(int j=0;j<=cnt;j++){
            w[j].cap=yy[j];
            w[j].flow=0;
        }
        int fx=w[i^1].cap;
        w[i^1].cap=0;
        int tmp=dinic();
        //printf("%d ",tmp);
        if (ans-tmp==fx) sy[w[i].to-m]=true;
    }
    for(int i=1;i<=m;i++){
        bool bb=1;
        for(int j=1;j<=qc[i][0];j++){
            if(!sy[qc[i][j]])bb=0;
        }
        if(bb){
            printf("%d ",i);
        }
    }
    printf("\n");
    for(int i=1;i<=n;i++){
        if(sy[i])printf("%d ",i);
    }
    printf("\n%d\n",v[0]-ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值