• 题解 【网络流24题】太空飞行计划


    【网络流24题】太空飞行计划

    Description

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

    Input

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

    Output

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

    Sample Input

    2 3
    10 1 2
    25 2 3
    5 6 7

    Sample Output

    1 2
    1 2 3
    17

    Hint

    Source

    网络流,最大权闭合图,网络最小割

    解析

    这题适合未很好理解最小割的同学(比如本蒟蒻)一刷。

    首先,这是建的残量网络(图片来自洛谷若有侵权请联系删除)

    其中,S到实验的边中,流量的意思是说,

    做这个实验的利润。

    那么流量为零时,则表这个实验没有做。

    而仪器到T的边的流量则表示这个仪器未付的钱。

    那么,如果存在一条从S到T的路径,

    就表示这个实验被选中,却没付仪器的钱!

    不付钱怎么行呢?【滑稽】

    所以,我们有两种选择:要么不做实验(即把S到实验的边割掉),

    或者老实付钱(即把仪器到T的边割掉)。

    而这两种选择都要花我们的钱(因为不做实验利润就没了,而仪器肯定得花钱啊)。

    直到S到T不连通(即不欠钱),再用利润总和减掉总流量就可以了。

    所以,我们求这张图上的最小割就行了。

    最后,注意读入(我是魔改快读)。

    上AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    struct node{
        int to,next,w;
    }e[100001];
    int n,m,s,t,maxn;
    int p[10001],c[10001];
    int head[10001],cnt=1;
    int dis[10001];
    
    const int INF=0x3f3f3f3f;
    
    void add(int x,int y,int w){
        e[++cnt].to=head[x];
        e[cnt].next=y;
        e[cnt].w=w;
        head[x]=cnt;
    }
    
    int read(int x){
        int sum=0,f=1;char ch=getchar();
        while(ch!='
    '&&ch!='
    '){
            while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
            while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
            add(x+1,f*sum+m+1,INF);add(f*sum+m+1,x+1,0);
            f=1;sum=0;
        }
        return f*sum;
    }
    
    bool bfs(){
        queue <int> que;
        memset(dis,0xff,sizeof(dis));
        dis[s]=0;
        que.push(s);
        while(!que.empty()){
            int x=que.front();
            que.pop();
            for(int i=head[x];i;i=e[i].to){
                int k=e[i].next;
                if(dis[k]>=0||!e[i].w) continue;
                dis[k]=dis[x]+1;
                que.push(k);
            }
        }
        if(dis[t]>0) return 1;
        return 0;
    }
    
    int dfs(int x,int mi){
        if(x==t) return mi;
        int ret=0;
        for(int i=head[x];i;i=e[i].to){
            int k=e[i].next;
            if(!e[i].w||dis[k]-1!=dis[x]) continue;
            if(ret=dfs(k,min(mi,e[i].w))){
                e[i].w-=ret;
                e[i^1].w+=ret;
                return ret;
            }
        }
        return 0;
    }
    
    void DINIC(){
        int ans=0,ret;
        while(bfs()){
            while((ret=dfs(s,INF))) ans+=ret;
        }
        for(int i=2;i<=m+1;i++){
            if(dis[i]>0) printf("%d ",i-1);
        }
        printf("
    ");
        for(int i=m+2;i<=m+n+2;i++){
            if(dis[i]>0) printf("%d ",i-m-1);
        }
        
        printf("
    %d
    ",maxn-ans);
        return ;
    }
    
    int main(){
        cin>>m>>n;
        s=1;t=m+n+2;
        char cc=getchar();
        for(int i=1;i<=m;i++){
            cin>>p[i];
            maxn+=p[i];
            read(i);
        }
        for(int i=1;i<=n;i++){
            cin>>c[i];
        }
        for(int i=1;i<=m;i++){
            add(1,i+1,p[i]);
            add(i+1,1,0);
        }
        for(int i=1;i<=n;i++){
            add(i+m+1,t,c[i]);
            add(t,i+m+1,0);
        }
        DINIC();
        return 0;
    }
  • 相关阅读:
    关闭窗体后,利用StreamWriter保存控件里面的数据
    ref传递
    C# 特性 Attribute
    关键字 new 的作用
    关键字 base 的作用
    关键字 this 的作用
    random类的使用
    数据库结果为 基于左右值排序的无限分类算法
    PHP显示日期、周几、农历初几、什么节日函数编程代码
    描述了say_hello函数的具体内容,调用zend_printf系统函数在php中打印字符串
  • 原文地址:https://www.cnblogs.com/zsq259/p/10510903.html
Copyright © 2020-2023  润新知