太空飞行计划问题
问题描述
W教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej 需要用到的仪器是I的子集Rj ∈ I。配置仪器Ik的费用为ck美元。实验Ej 的赞助商已同意为该实验结果支付pj 美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
编程任务
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
数据输入
由文件input.txt提供输入数据。文件第1行有2个正整数m和n。m是实验数,n是仪器数。接下来的m行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的n个数是配置每个仪器的费用。
结果输出
程序运行结束时,将最佳实验方案输出到文件output.txt中。第1行是实验编号;第2行是仪器编号;最后一行是净收益。
输入文件示例input.txt
2 3
10 1 2
25 2 3
5 6 7
输出文件示例output.txt
1 2
1 2 3
17
分析:
我们可以用最大权闭合图模型建图,把实验看作二分图的X集,仪器看作二分图的Y集。加个S,加个T,S向所有X集连边,边权为该实验的收入。所有仪器向T连边,边权为改仪器的花费。所有实验向其依赖的仪器连边,边权为inf
然后求最小割,答案就是所有实验收入之和Total - maxflow。
.......接下来让我们考虑一下为何能如此建图。
定义一个割划分出的S集为一个解,则割集容量之和为未选的实验的收入 + 被选的仪器的花费。这样Total – maxflow就代表被选的实验收入– 被选的仪器花费。
这样问题就转化成了最大化(Total – maxflow) ,也就是最小化maxflow。
So......you know.........
Code:
#include <cstdio> #include <cstring> using namespace std; const int inf = 1 << 30; const int maxn = 1010; const int maxm = 2020; inline int min(int a,int b) {return a < b ? a : b;} int tot; struct Edge { int v,w,nxt; }e[maxm]; int head[maxn]; int d[maxn],q[maxn]; int sta,end; inline void addedge(int u,int v,int d) { e[tot].v = v,e[tot].w = d,e[tot].nxt = head[u],head[u] = tot ++; e[tot].v = u,e[tot].w = 0,e[tot].nxt = head[v],head[v] = tot ++; } inline int bfs() { memset(d,-1,sizeof(d)); int r = d[q[0] = sta] = 0; for(int l = 0;l <= r;l ++) for(int k = head[q[l]]; ~k ;k = e[k].nxt) if(e[k].w && !~d[e[k].v]) d[q[++ r] = e[k].v] = d[q[l]] + 1; return ~d[end]; } int find(int x,int low) { if(x == end) return low; int a; for(int k = head[x]; ~k ;k = e[k].nxt) if(e[k].w && d[e[k].v] == d[x] + 1 && (a = find(e[k].v,min(low,e[k].w)))) { e[k].w -= a; e[k ^ 1].w += a; return a; } return 0; } int dinic() { int ret = 0,t; while(bfs()) while(t = find(sta,inf)) ret += t; return ret; } bool bLineBreak; void scan(int & n) { char c; n = 0; bLineBreak = 0; while(c = getchar(),c < '0' || c > '9') if(c == ' ') bLineBreak = 1; n = c - '0'; while(c = getchar(),(c >= '0' && c <= '9')) n = n * 10 + c - '0'; if(c == ' ') bLineBreak = 1; } inline void debug_dinic() { int n,m; scanf("%d%d",&n,&m); sta = 0,end =1; memset(head,-1,sizeof(head)); while(m --) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } printf(" %d ",dinic()); } inline void debug_scan() { int n; do { scan(n); printf("%d ",n); } while(!bLineBreak); } int experiment[maxn],vis[maxn]; int main() { int m,n,sum = 0; sta = 0,end = 1; scanf("%d%d",&m,&n); memset(head,-1,sizeof(head)); for(int i = 0;i < m;i ++) { int val,id; scanf("%d",&val); addedge(sta,i + 2,val),sum += val; do { scan(id); addedge(i + 2,m + 1 + id,inf); } while(!bLineBreak); } for(int i = 0;i < n;i ++) { int cost; scanf("%d",&cost); addedge(m + 2 + i,end,cost); } int ans = sum - dinic(); int cnt = 0; for(int k = head[0]; ~k ;k = e[k].nxt) if(e[k].w) { int id = e[k].v; experiment[cnt ++] = id; printf("%d ",id - 1); } putchar(' '); for(int i = 0;i < cnt;i ++) { for(int k = head[experiment[i]]; ~k ;k = e[k].nxt) { int v = e[k].v; if(vis[v]) continue; vis[v] = 1; if(!e[head[v]].w) printf("%d ",e[k].v - 1 - m); } } printf(" %d",ans); return 0; }