题解 P2762 【太空飞行计划问题】
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行是仪器编号;最后一行是净收益。
最大权闭合子图:源点连正点权(即实验收益),容量为正;负点权(器材花费)连汇点,容量为负的负(花费较总收益来说原本就是负的,连得时候连正的就行),有关系的实验和器材之间连边容量为正无穷,答案为全部实验收益 - 上面图跑出来的最小割
解释:正点之间与负点连边为INF最小割跑出来,要么割正边,意义就是舍弃这个实验,要么割负的负边,意义为买器材花钱,无论怎样,要是使得放弃实验钱与花的器材钱最少(也就是最小割),答案即为最优
Code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 10019,INF = 1e9;;
int nl,nr,nume = 1;
int s,t,maxflow;
int head[maxn],cur[maxn];
struct Node{
int v,dis,nxt;
}E[maxn << 2];
void add(int u,int v,int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
int d[maxn];
bool bfs(){
queue<int>Q;
memset(d,0,sizeof(d));
for(int i = 1;i <= t;i++)cur[i] = head[i];
Q.push(s);
d[s] = 1;
while(!Q.empty()){
int u = Q.front();
Q.pop();
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(E[i].dis && !d[v]){
d[v] = d[u] + 1;
Q.push(v);
if(v == t)return 1;
}
}
}
return 0;
}
int Dinic(int u,int flow){
if(u == t)return flow;
int rest = flow,k;
for(int i = cur[u];i != -1;i = E[i].nxt){
cur[u] = i;
int v = E[i].v;
if(E[i].dis && d[v] == d[u] + 1){
k = Dinic(v,min(rest,E[i].dis));
if(!k)d[v] = 0;
E[i].dis -= k;
E[i ^ 1].dis += k;
rest -= k;
}
if(rest == 0)break;
}
return flow - rest;
}
int tot,ans;
int main(){
memset(head,-1,sizeof(head));
nl = RD();nr = RD();
s = nl + nr + 1,t = s + 1;
int temp;
for(int i = 1;i <= nl;i++){
temp = RD();
tot += temp;
add(s,i,temp);
add(i,s,0);
char tools[10000];
memset(tools,0,sizeof tools);
cin.getline(tools,10000);
int ulen=0,tool;
while (sscanf(tools+ulen,"%d",&tool)==1)//之前已经用scanf读完了赞助商同意支付该实验的费用
{//tool是该实验所需仪器的其中一个
//这一行,你可以将读进来的编号进行储存、处理,如连边。
add(i,tool + nl,INF);
add(tool + nl,i,0);
if (tool==0)
ulen++;
else {
while (tool) {
tool/=10;
ulen++;
}
}
ulen++;
}
}
for(int i = 1 + nl;i <= nr + nl;i++){
temp = RD();
add(i,t,temp);
add(t,i,0);
}
int flow = 0;
while(bfs())while(flow = Dinic(s,INF))maxflow += flow;
ans = tot - maxflow;
if(ans < 0){
printf("0\n");
return 0;
}
for(int i = 1;i <= nl;i++){
if(d[i])printf("%d ",i);
}
printf("\n");
for(int i = 1 + nl;i <= nr + nl;i++){
if(d[i])printf("%d ",i - nl);
}
printf("\n");
printf("%d\n",ans);
return 0;
}