程设老师最近要进行一项邪恶的实验,这个实验一共持续 天,第 天需要 个研究生来给他搬砖。研究生毕竟也是人,所以雇佣研究生是需要钱的,机智的程设老师已经联系好了 所大学,第 所大学共有 个研究生,同时雇佣这所大学的一个研究生需要 元钱。
一天下来给他搬砖的所有研究生都会进入濒死状态。濒死状态的研究生,毫无疑问,就不能再进行工作了。但是机智的老师早早联系好了 家医院,第 家医院医治一个濒死的研究生需要 天,并且需要 元钱。
链接
题解
建立费用流模型。
为每一天建两个点,一个表示这一天开始时可用的研究生,另一个表示这一天结束时濒死的研究生。两点之间连一条边,流量上下界均为这一天所需的研究生数量 ,费用为零。
显然,第一天雇佣所有的研究生,每一天未使用的研究生留到下一天,和在每一天分别雇佣研究生是等价的。从源点向第一天可用研究生的点连 条边,容量为 ,费用为一个 。
从第 天表示濒死的研究生的点向第 天表示可用研究生的点连一条边,表示医治一些研究生,容量为正无穷,费用为 。
对上下界的处理:从源点到表示每天濒死研究生的点连一条边,容量为 ,从表示每天可用研究生的点向汇点连一条边,容量为 ,最终流量为 则有解。
代码
#include <cstdio>
#include <climits>
#include <algorithm>
#include <queue>
const int MAXN = 50;
const int MAXM = 50;
const int MAXK = 50;
struct Node {
struct Edge *e, *in;
bool q;
int d, f;
} N[MAXN * 2 + 2];
struct Edge {
Node *s, *t;
int f, c, w;
Edge *next, *r;
Edge(Node *s, Node *t, const int c, const int w) : s(s), t(t), f(0), c(c), w(w), next(s->e) {}
};
inline void addEdge(const int s, const int t, const int c, const int w) {
N[s].e = new Edge(&N[s], &N[t], c, w);
N[t].e = new Edge(&N[t], &N[s], 0, -w);
(N[s].e->r = N[t].e)->r = N[s].e;
}
inline void edmondskarp(const int s, const int t, const int n, int &flow, int &cost) {
flow = cost = 0;
while (1) {
for (int i = 0; i < n; i++) {
N[i].in = NULL;
N[i].q = false;
N[i].d = INT_MAX;
N[i].f = 0;
}
std::queue<Node *> q;
q.push(&N[s]);
N[s].d = 0, N[s].f = INT_MAX;
while (!q.empty()) {
Node *v = q.front();
q.pop();
v->q = false;
for (Edge *e = v->e; e; e = e->next) if (e->f < e->c && e->t->d > v->d + e->w) {
e->t->d = v->d + e->w;
e->t->f = std::min(v->f, e->c - e->f);
e->t->in = e;
if (!e->t->q) {
e->t->q = true;
q.push(e->t);
}
}
}
if (N[t].d == INT_MAX) return;
for (Edge *e = N[t].in; e; e = e->s->in) {
e->f += N[t].f;
e->r->f -= N[t].f;
}
flow += N[t].f;
cost += N[t].f * N[t].d;
}
}
inline void clear(const int n) {
for (int i = 0; i < n; i++) {
Edge *next;
for (Edge *&e = N[i].e; e; next = e->next, delete e, e = next);
}
}
int main() {
int t;
scanf("%d", &t);
for (int i = 1; i <= t; i++) {
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
const int s = 0, t = n * 2 + 1;
int sum = 0;
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
addEdge(i, t, x, 0);
addEdge(s, i + n, x, 0);
sum += x;
if (i != n) addEdge(i, i + 1, INT_MAX, 0);
}
for (int i = 1; i <= m; i++) {
int a, b;
scanf("%d %d", &a, &b);
addEdge(s, 1, a, b);
}
for (int i = 1; i <= k; i++) {
int a, b;
scanf("%d %d", &a, &b), a++;
for (int j = 1; j <= n - a; j++) addEdge(j + n, j + a, INT_MAX, b);
}
int flow, cost;
edmondskarp(s, t, n * 2 + 2, flow, cost);
printf("Case %d: ", i);
if (flow == sum) printf("%d\n", cost);
else puts("impossible");
clear(n * 2 + 2);
}
return 0;
}