思路1
一个很直观地思路,拆点,跑上下界费用流。
思路2
直接建图吧,把 $x$ 拆成 $x$ 和 $x’$。
- $s—>1\sim n$ 流量 $1$,费用 $0$
- $s—>1’\sim n’$ 流量 $1$,费用 $a_i$
- $1’\sim n’ —> t$ 流量 $1$,费用 $0$
- $u—>v’$ 流量 $1$,费用 $w$
首先,假设现在有一个可行流,满流。
对于节点 $1’$,它连向 $t$ 的边一定有,而唯一能到达它的是边 $(s,1’,1,a_1)$。
接下来观察点 $1$,它可以连向 $2’\sim n’$,假设它连向 $k’$,那么接下来观察 $k$,如此往复,得到一条链。
那么接下来往哪走呢?在上面的操作中,当节点经过 $x$ 是,$x’$ 也一定会经过。找到编号最小的 $y$ 使得 $y$ 和 $y’$ 都没有被经过,继续进行如上的找链操作。
于是一个满流的可行流就转化成了原问题的可行方案。
code
思路2的代码。
#include <bits/stdc++.h>
using namespace std;
const int N = 5010, M = 100010, inf = 0x3f3f3f3f;
int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
memset(incf, 0, sizeof incf);
memset(d, 0x3f, sizeof d);
int hh = 0, tt = 1;
q[0] = S, d[S] = 0, incf[S] = inf;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (f[i] && d[j] > d[t] + w[i])
{
d[j] = d[t] + w[i];
pre[j] = i;
incf[j] = min(incf[t], f[i]);
if (!st[j])
{
q[tt ++ ] = j;
if (tt == N) tt = 0;
st[j] = true;
}
}
}
}
return incf[T] > 0;
}
void EK(int& flow, int& cost)
{
flow = cost = 0;
while (spfa())
{
flow += incf[T], cost += incf[T] * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1] )
{
f[pre[i]] -= incf[T];
f[pre[i] ^ 1] += incf[T];
}
}
}
int main()
{
scanf("%d%d", &n, &m);
int tot = 2 * n;
S = 0, T = ++ tot;
vector<int> a(n + 10);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ ) add(S, n + i, 1, a[i]), add(S, i, 1, 0), add(n + i, T, 1, 0);
for (int i = 1; i <= m; i ++ ) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
if (u > v) swap(u, v);
add(u, v + n, 1, w);
}
int flow, cost;
EK(flow, cost);
printf("%d\n", cost);
return 0;
}