# P2050 [NOI2012]美食节

$2 \leq M \leq 9,1 \leq N \leq 60,1 \leq T \leq 1000$

#### 费用提前计算

$i$ 位车主向第 $j$ 位维修人员拆成的第 $k$ 个点连边，容量为 $1$ ，费用为 $k \times t_{i,j}$

$N \leq 40,M \leq 100$

P2053 [SCOI2007]修车 的建图方式，但是硬求最小费用最大流只能拿到 $60$ 分。

#### 动态开点

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 6, M = 4e7 + 6, inf = 0x3f3f3f3f;
int n, m, s, t, ans, d[N], pre[N], now[N], num, p[N];
int Head[N], Edge[M], Leng[M], Cost[M], Next[M], tot = 1;
bitset<N> v;
int a[46][106];

inline void add(int x, int y, int z, int w) {
Edge[++tot] = y;
Leng[tot] = z;
Cost[tot] = w;
}

inline bool spfa() {
v.reset();
memset(d, 0x3f, sizeof(d));
queue<int> q;
q.push(s);
v[s] = 1;
d[s] = 0;
now[s] = inf;
while (q.size()) {
int x = q.front();
q.pop();
v[x] = 0;
for (int i = Head[x]; i; i = Next[i]) {
int y = Edge[i], z = Leng[i], w = Cost[i];
if (!z || d[y] <= d[x] + w) continue;
d[y] = d[x] + w;
now[y] = min(now[x], z);
pre[y] = i;
if (!v[y]) {
q.push(y);
v[y] = 1;
}
}
}
return d[t] != inf;
}

inline void upd() {
ans += d[t] * now[t];
int x = t;
while (x != s) {
int i = pre[x];
Leng[i] -= now[t];
Leng[i^1] += now[t];
x = Edge[i^1];
}
x = Edge[pre[t]^1];
p[++num] = p[x];
add(num, t, 1, 0);
add(t, num, 0, 0);
for (int i = Head[x]; i; i = Next[i]) {
int y = Edge[i], w = Cost[i^1];
if (y == t) continue;
w += a[y][p[x]];
add(y, num, 1, w);
add(num, y, 0, -w);
}
}

int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
add(0, i, x, 0);
add(i, 0, 0, 0);
}
num = t = n + m + 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
int x;
scanf("%d", &x);
a[i][j] = x;
add(i, n + j, 1, x);
add(n + j, i, 0, -x);
}
for (int i = 1; i <= m; i++) {
add(n + i, t, 1, 0);
add(t, n + i, 0, 0);
p[n+i] = i;
}
while (spfa()) upd();
cout << ans << endl;
return 0;
}
posted @ 2019-03-10 00:38 xht37 阅读(...) 评论(...) 编辑 收藏