【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

题解

先吐槽一下这一个输入,调了好长时间还是输不进来.最后翻的题解才码出来,还是我太菜了.。
说一下题解:
这是一个最大权闭合图(好绕口)…
什么是最大权闭合图呢?
就是对于一个图的一个子图,对于节点 u u ,那么它的后继节点v也属于这个子图,那么这个子图就是闭合图。当这个子图的权最大时就叫最大权闭合图.
对于最大权闭合图,我们有一下的性质:
最小割所产生的两个集合中,其源点S所在集合(除去S)为最大权闭合图。
答案数等于靠近源点最小割一边的点数,最大利益=所有点正权值之和-最小割.
所以这个题就要用最小割来写了。
建图:
S->实验:边权为实验费用
实验->所用到的器材:边权为INF
器材->T:边权为器材费用
跑一边最大流就可以了(最大流等于最小割)

代码

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define INF 0x7f7f7f7f
const int MAXN=1e6;
using namespace std;
struct Edge
{
    int next,to,c,f;
}edge[MAXN];
int Num1,Num2,s,t,n,m;
int head[MAXN],level[MAXN],cnt;
int val[MAXN],cost[MAXN];
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();       
    } 
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
inline bool EXrnum(int &res) //遇到'\n'跳出的读入优化
{
    res = 0; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '\n') return false; ch = getchar(); }
    while (isdigit(ch)) { res = res * 10 + ch - '0'; ch = getchar(); }
    if (ch == '\n') return false; else return true;
}
inline void Add_Edge(int u,int v,int c,int f)
{
    edge[cnt]=(Edge){head[u],v,c,f};
    head[u]=cnt++;
} 
bool bfs()
{
    memset(level,0,sizeof(level));
    level[s]=1;
    queue<int> que;
    que.push(s);
    while(!que.empty())
    {
        int c=que.front();
        que.pop();
        for(int i=head[c];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(level[v]==0 && edge[i].c>edge[i].f)
            {
                level[v]=level[c]+1;
                que.push(v);    
            }   
        } 
    }
    if(level[t]==0)
        return 0;
    else
        return 1;
}
int dfs(int u,int f)
{
    if(u==t) return f;
    int adf=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(level[v]==level[u]+1 && edge[i].c>edge[i].f)
        {
            int temp=dfs(v,min(f-adf,edge[i].c-edge[i].f));
            edge[i].f+=temp;
            edge[i^1].f-=temp;
            adf+=temp;
        }
    }
    return adf;
}
int Dinic()
{
    int maxf=0;
    while(bfs())
    {
        maxf+=dfs(s,1e9);
    }   
    return maxf;
}
int sum; 
int main()
{
    memset(head,-1,sizeof(head));
    m=read(),n=read();
    s=0,t=n+m+1;    
    for(int i=1;i<=m;i++)
    {
        char ch;
        scanf("%d",&val[i]);
        sum+=val[i];
        Add_Edge(s,i,val[i],0);
        Add_Edge(i,s,0,0); 
        int now=0;
        while(EXrnum(now))
        {
            Add_Edge(i,now+m,INF,0);
            Add_Edge(now+m,i,0,0); 
        } 
        Add_Edge(i,now+m,INF,0);
        Add_Edge(now+m,i,0,0);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&cost[i]);
        Add_Edge(i+m,t,cost[i],0);
        Add_Edge(t,i+m,0,0); 
    }
    int ans=Dinic();
    for(int i=1;i<=m;i++) 
    {
        if(level[i]!=0)
            printf("%d ",i);
    }
    printf("\n");
    for(int i=1;i<=n;i++)
    {
        if(level[i+m]!=0)
            printf("%d ",i);
    }
    printf("\n");
    printf("%d",sum-ans);
    return 0;
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值