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

时间:2024-04-15 08:04:32

#6001. 「网络流 24 题」太空飞行计划

内存限制:256 MiB时间限制:1000 ms标准输入输出
题目类型:传统评测方式:Special Judge
上传者: 匿名

题目描述

W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 E={E1,E2,⋯,Em} E = \{ E_1, E_2, \cdots, E_m \}E={E​1​​,E​2​​,⋯,E​m​​},和进行这些实验需要使用的全部仪器的集合 I={I1,I2,⋯,In} I = \{ I_1, I_2, \cdots, I_n \}I={I​1​​,I​2​​,⋯,I​n​​}。实验 Ej E_jE​j​​ 需要用到的仪器是 I II 的子集 Rj⊆I R_j \subseteq IR​j​​⊆I。

配置仪器 Ik I_kI​k​​ 的费用为 ck c_kc​k​​ 美元。实验 Ej E_jE​j​​ 的赞助商已同意为该实验结果支付 pj p_jp​j​​ 美元。W 教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。

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

输入格式

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

输出格式

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

样例

样例输入

2 3
10 1 2
25 2 3
5 6 7

样例输出

1 2
1 2 3
17

数据范围与提示

1≤n,m≤50

题目链接:https://loj.ac/problem/6001

题意:有m个太空飞行计划,执行一个计划可以获得xi的赞助,但是每个飞行计划需要一些仪器。每个仪器配置需要花费xi。求执行一些天空飞行计划使得收益最大。

思路:最大权闭合图。最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。

转载一篇苣苣的博客:最大权闭合图

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn=2e2+,maxm=1e5+,inf=0x3f3f3f3f,mod=1e9+;
const ll INF=1e18+;
priority_queue<P,vector<P>,greater<P> >q;
struct edge
{
int from,to;
int cap;
int rev;
};
int n;
vector<edge>G[maxn];
int level[maxn];
int iter[maxn];
void addedge(int u,int v,int c)
{
edge e;
e.from=u,e.to=v,e.cap=c,e.rev=G[v].size();
G[u].push_back(e);
e.from=v,e.to=u,e.cap=,e.rev=G[u].size()-;
G[v].push_back(e);
}
int bfs(int s,int t)
{
memset(level,-,sizeof(level));
queue<int>q;
level[s]=;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=; i<G[u].size(); i++)
{
edge e=G[u][i];
if(e.cap>&&level[e.to]<)
{
level[e.to]=level[u]+;
q.push(e.to);
}
}
}
return level[t]!=-?:;
}
int dfs(int u,int t,int f)
{
if(u==t||f==) return f;
int flow=;
for(int &i=iter[u]; i<G[u].size(); i++)
{
edge e=G[u][i];
if(e.cap>&&level[u]+==level[e.to])
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>)
{
G[u][i].cap-=d;
G[e.to][e.rev].cap+=d;
flow+=d;
f-=d;
if(f==) break;
}
}
}
return flow;
}
int max_flow(int s,int t)
{
int flow=;
while(bfs(s,t))
{
memset(iter,,sizeof(iter));
flow+=dfs(s,t,inf);
}
return flow;
}
int a[maxn],b[maxn];
int main()
{
int m,n;
scanf("%d%d",&m,&n);
int s=,t=n+m+;
int ans=;
for(int i=; i<=m; i++)
{
scanf("%d",&a[i]);
ans+=a[i];
addedge(s,i,a[i]);
int num=;
char ch;
do
{
ch=getchar();
if(ch==' '||ch=='\n')
{
if(num) addedge(i,num+m,inf);
num=;
}
else num=num*+(ch-'');
}
while(ch!='\n');
}
for(int i=; i<=n; i++)
{
scanf("%d",&b[i]);
addedge(i+m,t,b[i]);
}
ans-=max_flow(s,t);
for(int i=;i<=m;i++)
if(level[i]!=-) printf("%d ",i);
cout<<endl;
for(int i=;i<=n;i++)
if(level[i+m]!=-) printf("%d ",i);
cout<<endl;
printf("%d\n",ans);
}

最大权闭合图