NKOJ1937-太空飞行计划
时间限制 : - MS 空间限制 : 65536 KB
问题描述
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集Rj属于I。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。
W教授的任务是找出一个有效算法,确定在一次太飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
输出最多能得到的净获利。
输入格式
第1行有2 个正整数m和n。m是实验数,n是仪器数。(n,m<=50)
接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。
最后一行的n个数是配置每个仪器的费用。
输出格式
一行,包含一个整数,表示最大净收益
样例输入 1
2 3
10 1 2
25 2 3
5 6 7
样例输出 1
17
样例输入 2
6 22
9 2 7
4 5 6 7 9
3 2
10 2
10 3 6 11
8 4 5 6 9
6 1 3 7 10 2 5 8 3 5 7 6 10 6 6 2 4 6 5 5 3 9
样例输出 2
16
题解
显然的网络流
建图:实验和仪器各放一边,加上源点s和汇点t
1:将源点与每个实验连边,容量为该实验的收益;
2:将每个仪器与汇点连边,容量为该仪器的费用;
3:将每个实验到其所需要的仪器连接一条容量为inf的边。
分析:
设一个割划分出的集合为一个解,tot为实验收益总和,那么集合的容量之和就是未选实验的收益 + 被选仪器的费用,记为 cut,那么tot - cut 就是被选的实验的收益 - 被选仪器的费用,即净收益,因tot为定值,要想净收益最大,cut就要最小,即最小割=最大流;
因此,最大净收益=实验收益总和-最大流。
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int inf=1e9;
const int N=123;
int n,m,t,x,tot=0,ans=0;
int g[N][N],dis[N],cnt[N];
char c;
inline void R(int &x) {
c=getchar();
while(c<'0'||'9'<c) c=getchar();
for(x=c-'0',c=getchar();'0'<=c&&c<='9';x=x*10+c-'0',c=getchar());
}
int SAP(int u,int flow) {
if(u==t) return flow;
int delta=0;
for(int v=1;v<=t;v++)
if(g[u][v]>0&&dis[u]==dis[v]+1) {
int temp=SAP(v,min(flow-delta,g[u][v]));
g[u][v]-=temp;
g[v][u]+=temp;
delta+=temp;
if(delta==flow||dis[0]>=t) return delta;
}
if(dis[0]>=t) return delta;
cnt[dis[u]]--;
if(cnt[dis[u]]==0) dis[1]=t;
dis[u]+=1;
cnt[dis[u]]++;
return delta;
}
int main() {
R(m); R(n);
t=m+n+1;
for(int i=1;i<=m;i++) {
R(g[0][i]);
tot+=g[0][i];
while(c!='\n') {
R(x);
g[i][m+x]=inf;
}
}
for(int i=1;i<=n;i++) {
R(x);
g[m+i][t]=x;
}
while(dis[0]<t) ans+=SAP(0,inf);
cout<<tot-ans;
}