LibreOJ #6001. 「网络流 24 题」太空飞行计划 最大权闭合图
Posted GeekZRF
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LibreOJ #6001. 「网络流 24 题」太空飞行计划 最大权闭合图相关的知识,希望对你有一定的参考价值。
#6001. 「网络流 24 题」太空飞行计划
题目描述
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。求执行一些天空飞行计划使得收益最大。
思路:最大权闭合图。对于一个割割成2个顶点集合,让其中一个的执行飞行计划,另一个不执行。这里不妨假设包含s的集合执行飞行计划,包含t的集合不执行。由于是最小割,就需要搞清楚最小化的是什么。看起来损失是一个比较合理的解释。先不考虑仪器和计划之间的关系,而是先考虑怎么归到最小割这个问题上面。每一个仪器和计划对应一个顶点。如果仪器和s相连,也就是配置了仪器,产生了损失,因此和s之间连一条容量为配置花费为容量的边。由于如果和t相连,也就是不配置仪器,就没有收益也没有损失。如果计划和s相连,也就是执行了计划,就可以看成是产生了负的损失,因为最大流最小割定理用最大流求解的话,是不能有容量为负的边。因此,我们认为计划不执行则损失了收益。接下来明天发。。。
代码:
#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+100,maxm=1e5+100,inf=0x3f3f3f3f,mod=1e9+7; const ll INF=1e18+7; 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=0,e.rev=G[u].size()-1; G[v].push_back(e); } int bfs(int s,int t) { memset(level,-1,sizeof(level)); queue<int>q; level[s]=0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0; i<G[u].size(); i++) { edge e=G[u][i]; if(e.cap>0&&level[e.to]<0) { level[e.to]=level[u]+1; q.push(e.to); } } } return level[t]!=-1?1:0; } int dfs(int u,int t,int f) { if(u==t||f==0) return f; int flow=0; for(int &i=iter[u]; i<G[u].size(); i++) { edge e=G[u][i]; if(e.cap>0&&level[u]+1==level[e.to]) { int d=dfs(e.to,t,min(f,e.cap)); if(d>0) { G[u][i].cap-=d; G[e.to][e.rev].cap+=d; flow+=d; f-=d; if(f==0) break; } } } return flow; } int max_flow(int s,int t) { int flow=0; while(bfs(s,t)) { memset(iter,0,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=0,t=n+m+1; int ans=0; for(int i=1; i<=m; i++) { scanf("%d",&a[i]); ans+=a[i]; addedge(s,i,a[i]); int num=0; char ch; do { ch=getchar(); if(ch==‘ ‘||ch==‘\n‘) { if(num) addedge(i,num+m,inf); num=0; } else num=num*10+(ch-‘0‘); } while(ch!=‘\n‘); } for(int i=1; i<=n; i++) { scanf("%d",&b[i]); addedge(i+m,t,b[i]); } ans-=max_flow(s,t); for(int i=1;i<=m;i++) if(level[i]!=-1) printf("%d ",i); cout<<endl; for(int i=1;i<=n;i++) if(level[i+m]!=-1) printf("%d ",i); cout<<endl; printf("%d\n",ans); }
以上是关于LibreOJ #6001. 「网络流 24 题」太空飞行计划 最大权闭合图的主要内容,如果未能解决你的问题,请参考以下文章