LOJ 6001 【网络流24题】太空飞行计划
题面
【问题描述】
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合(E={E_1,E_2,…,E_m}),和进行这些实验需要使用的全部仪器的集合(I={I_1,I_2,…I_n})。实验(E_j) 需要用到的仪器是I 的子集(R_j⊆I)。配置仪器(I_k) 的费用为(c_k) 美元。实验(E_j) 的赞助商已同意为该实验结果支付(p_j) 美元。W 教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。【编程任务】
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。【输入格式】
第1 行有2 个正整数(m) 和(n)。(m) 是实验数,(n) 是仪器数。接下来的(m) 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的(n) 个数是配置每个仪器的费用。【输出格式】
第1 行是实验编号;第2 行是仪器编号;最后一行是净收益。【样例输入】
2 3 10 1 2 25 2 3 5 6 7
【样例输出】
1 2 1 2 3 17
题解
还记得“最大权闭合子图”么!
首先我们假设所有的报酬都拿到了,并且一分成本都没花;然后我们来计算最小的损失是多少。损失有两种:买仪器的成本和未完成实验损失的报酬。最小损失可以用最小割来求。
我们这样建图:
- 源点到每个实验连一条边,容量为实验报酬。
- 每个仪器到汇点连一条边,容量为仪器成本。
- 每个实验向它依赖的仪器连一条边,容量为INF(保证它不被割掉)。
这样求得的最小割,就相当于在“损失实验报酬”和“损失仪器成本”中做出了选择,得到的最小损失。那么所有报酬 - 最小损失就是答案。
那么怎样输出具体方案呢?把在残量网络上(即满流的边不算边)和源点相连的实验算作要进行的实验就可以了。也就是说,(对于Dinic算法)lev不为-1的实验就是要进行的实验。
吐槽:需要手动判断是否换行的输入数据真是太鬼畜了!
我犯过的错误:判断是否与源点连通时把if(lev[u] != -1)写成if(lev[u])……
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
#define space putchar(' ')
#define enter putchar('
')
template <class T>
bool read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
else if(c == EOF) return 0;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
return 1;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 205, M = 200005, INF = 0x3f3f3f3f;
int n, m, s, t, ans, sum;
int val[N];
int ecnt = 1, adj[N], cur[N], nxt[M], go[M], cap[M];
int que[N], qr, lev[N];
bool chosen[N];
void add(int u, int v, int w){
go[++ecnt] = v;
cap[ecnt] = w;
nxt[ecnt] = adj[u];
adj[u] = ecnt;
}
bool bfs(){
for(int i = 1; i <= n; i++)
cur[i] = adj[i], lev[i] = -1;
que[qr = 1] = s, lev[s] = 0;
for(int ql = 1; ql <= qr; ql++){
int u = que[ql];
for(int e = adj[u], v; e; e = nxt[e]){
if(cap[e] && lev[v = go[e]] == -1){
lev[v] = lev[u] + 1;
que[++qr] = v;
if(v == t) return 1;
}
}
}
return 0;
}
int dinic(int u, int flow){
if(u == t) return flow;
int delta, ret = 0;
for(int &e = cur[u], v; e; e = nxt[e])
if(cap[e] && lev[v = go[e]] > lev[u]){
delta = dinic(v, min(flow - ret, cap[e]));
if(delta){
cap[e] -= delta;
cap[e ^ 1] += delta;
ret += delta;
if(ret == flow) return flow;
}
}
lev[u] = -1;
return ret;
}
template <class T>
bool tread(T &x){
char c;
while(c = getchar(), c < '0' || c > '9');
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
return c != '
' && c != '
';
}
int main(){
read(m), read(n);
s = n + m + 1, t = n + m + 2;
for(int i = 1, val, tmp; i <= m; i++){
tmp = tread(val);
add(s, i, val), add(i, s, 0);
sum += val;
if(tmp) do{
tmp = tread(val);
add(i, val + m, INF), add(val + m, i, 0);
} while(tmp);
}
for(int i = 1, val; i <= n; i++)
read(val), add(i + m, t, val), add(t, i + m, 0);
n = n + m + 2;
while(bfs()) ans += dinic(s, INF);
for(int i = 1; i <= m; i++)
if(lev[i] != -1){
write(i), space;
for(int e = adj[i]; e; e = nxt[e])
if(!(e & 1)) chosen[go[e]] = 1;
}
enter;
for(int i = m + 1; i <= n - 2; i++)
if(chosen[i]) write(i - m), space;
enter;
write(sum - ans), enter;
return 0;
}