太空飞行计划问题(最大流)

Posted hsez-cyx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了太空飞行计划问题(最大流)相关的知识,希望对你有一定的参考价值。

太空飞行计划问题(luogu)

Description

 

题目描述

 

W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。

现已确定了一个可供选择的实验集合 E={E1?,E2?,?,Em?},和进行这些实验需要使用的全部仪器的集合 I={I1?,I2?,?,In?}。

实验 Ej? 需要用到的仪器是 I 的子集 Rj?I。

配置仪器 I_k? 的费用为 c_k? 美元。实验 E_j 的赞助商已同意为该实验结果支付 p_j 美元。W 教授的任务是找出一个有效算法,

确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。

这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。

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

 

输入格式

 

第 1 行有 2 个正整数 m 和 n。m 是实验数,n 是仪器数。接下来的 m 行,每行是一个实验的有关数据。

第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的 n 个数是配置每个仪器的费用。

 

输出格式

 

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

 

Solution

这道题有一个隐形条件: p 和 c 都大于等于0

首先假设不需要支付配置仪器的费用,那么我们可以赚 $sumlimits_{i=1}^{m}$p_i。

但一部分钱需要用于配置仪器,那么我们建一张图,

将源点与实验连一条容量为 p_i 的边,表示做该实验最多获得 P-i 元;

将实验与每个它需要的仪器连一条容量无限大的边,表示该实验获得的钱可用于支付配置该仪器的费用,

别的实验不用跟这个仪器连边,因为如果所有需要该仪器的实验获得的钱都不够配置它的话,还不如不买它,不做需要它的实验;

将仪器与汇点连一条容量为 c-i 的边,表示它需要 c-i 元。

这个大佬的图好

该图的最大流表示 用于配置仪器的费用 + 不如不做的实验的总收益

于是答案为 $sumlimits_{i=1}^{m}$p_i -最大流

方案先求出需要的仪器:去掉仪器 i 与汇点的边后求出的最大流是否与原最大流差值为 c-i

然后推出可以做的实验

 

Code

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=110;
int m,n,cost[N],p[N],b[N][N],s,t,sum;
int head[N],ver[N*N],d[N],nxt[N*N],tot=1,edge[N*N],flow,maxflow;
bool sel[N];
void add(int x,int y,int w)
{
    ver[++tot]=y,nxt[tot]=head[x],head[x]=tot,edge[tot]=w;
    ver[++tot]=x,nxt[tot]=head[y],head[y]=tot,edge[tot]=0;
}
bool bfs()
{
    memset(d,0,sizeof(d));
    queue <int> q;
    q.push(s),d[s]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x],y;i;i=nxt[i])
            if(edge[i]>0 && !d[y=ver[i]])
            {
                d[y]=d[x]+1;
                q.push(y);
                if(y==t) return 1;
            }
    }
    return 0;
}
int dinic(int x,int flow)
{
    if(x==t) return flow;
    int rest=flow;
    for(int i=head[x],y;i && rest;i=nxt[i])
        if(edge[i]>0 && d[y=ver[i]]==d[x]+1)
        {
            int kk=dinic(y,min(edge[i],rest));
            if(!kk) d[y]=0;
            edge[i]-=kk,edge[i^1]+=kk;
            rest-=kk;
        }
    return flow-rest;
}
int main()
{
    scanf("%d%d",&m,&n);
    s=0,t=n+m+1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p[i]);
        sum+=p[i];
        add(s,i,p[i]);
        char tools[10000];
        memset(tools,0,sizeof tools);
        cin.getline(tools,10000);
        int ulen=0,tool;
        while(sscanf(tools+ulen,"%d",&tool)==1)
        {
            b[i][++b[i][0]]=tool;
            add(i,tool+m,1<<30);
            if(tool==0) ulen++;
            else while (tool) tool/=10,ulen++;
            ulen++;
        }
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&cost[i]);
        add(i+m,t,cost[i]);
    }
    while(bfs()) while(flow=dinic(s,1<<30)) maxflow+=flow;
    for(int i=1;i<=n;i++)
    {
        memset(head,0,sizeof(head)),tot=1;
        for(int j=1;j<=m;j++)
        {
            add(s,j,p[j]);
            for(int k=1;k<=b[j][0];k++)
                add(j,b[j][k]+m,1<<30);
        }
        for(int j=1;j<=n;j++)
            if(j!=i) add(j+m,t,cost[j]);
        int ans=0; 
        while(bfs()) while(flow=dinic(s,1<<30)) ans+=flow;
        if(maxflow-ans==cost[i]) sel[i]=true;
    }
    for(int i=1;i<=m;i++)
        while(p[i]<0);
    for(int i=1;i<=n;i++)
        while(cost[i]<0);
    for(int i=1;i<=m;i++)
    {
        bool flag=true;
        for(int j=1;j<=b[i][0];j++)
            if(!sel[b[i][j]])
            {
                flag=false;
                break;
            }
        if(flag) printf("%d ",i);
    }
    puts("");
    for(int i=1;i<=n;i++)
        if(sel[i]) printf("%d ",i);
    printf("
%d
",sum-maxflow);
    return 0;
}

 

 

 

 

以上是关于太空飞行计划问题(最大流)的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ #6001. 「网络流 24 题」太空飞行计划 最大权闭合图

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

刷题总结——太空飞行计划(最大权闭合子图用最大流解决)

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

luogu P2762 太空飞行计划问题

[luoguP2762] 太空飞行计划问题(最大权闭合图—最小割—最大流)