此題是以《NOI2008志願者招募》爲背景的,預做此題需要先體會《志願者招募》的思想
只不過由線性結構變爲樹形結構,但是問題的本質沒有變,都是一個元素影響連續的若干個位置,構圖的本質都是使每個變量x出現分別以+和-的形式出現在兩個恆等式中,由此可以看做x變量從+式流入-式流出。設藥品爲x,爲每條河建立一個等式,則x出現在了u的父邊到v的父邊路徑上的所有等式中,因此用每個點的父邊減去所有孩子的邊(設根節點的父邊爲全0,即沒有限制)得到n個等式,且所有變量x、y均以+和-的形式各出現一次,由此可以構圖。若求出最大流==源點的最大容量則最小費用即爲解,否則無解。
《志願者招募》題解寫的不錯,體會一下x、y變量的作用應該就會構圖了,構圖如下:
class node {
int be, ne;
int id, val;
node(int b, int e, int v) {
be = b;
ne = e;
val = v;
}
}
node buf[] = new node[maxn];
int E[] = new int[maxn], len;
void add(int a, int b, int v) {
buf[len] = new node(b, E[a], v);
id[b] = len;
E[a] = len++;
}
Scanner scan = new Scanner(System.in);
int n, m, id[] = new int[maxn], sum;
boolean is[] = new boolean[maxn];
MINCOST sp = new MINCOST();
void init() {
sp.init();
len = 0;
Arrays.fill(E, -1);
Arrays.fill(is, true);
sum = 0;
}
void run() {
int cas = scan.nextInt();
for (int k = 1; k <= cas; k++) {
System.out.print("Case #" + k + ": ");
n = scan.nextInt();
init();
int a, b, v;
for (int i = 1; i < n; i++) {
a = scan.nextInt();
b = scan.nextInt();
v = scan.nextInt();
is[a] = false;
add(b, a, v);
}
int root = -1;
for (int i = 1; i <= n; i++)
if (is[i]) {
root = i;
break;
}
id[root] = n - 1;
int s, t, l, c;
m = scan.nextInt();
for (int i = 0; i < m; i++) {
s = scan.nextInt();
t = scan.nextInt();
l = scan.nextInt();
c = scan.nextInt();
sp.addcap(id[s], id[t], l, c);
}
int temp = 0;
for (int i = E[root]; i != -1; i = buf[i].ne) {
temp += buf[i].val;
sp.addcap(n - 1, i, inf, 0);
dfs(i);
}
sp.addcap(n - 1, n + 1, temp, 0);
int ans = sp.solve(n, n + 1);
if (sp.maxflow == sum)
System.out.println(ans);
else
System.out.println(-1);
}
}
void dfs(int a) {
int temp = buf[a].val;
int v = buf[a].be;
for (int i = E[v]; i != -1; i = buf[i].ne) {
sp.addcap(a, i, inf, 0);
temp -= buf[i].val;
dfs(i);
}
if (temp > 0) {
sp.addcap(n, a, temp, 0);
sum += temp;
} else
sp.addcap(a, n + 1, -temp, 0);
}