最小割:
对于 (w_i imes(a_u-a_v)), 拆成 (w_ia_u) 和 (-w_ia_v), 这样就把权重拆到了点上。
首先显然地, 每个点的权值都在 [0,n-1] 之间, 于是可以跑最小割。
要保证网络图里的边权为整数, 给所有边加上同一个较大值即可。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 23, M = 1e5+23;
const LL bignum = 4e11, inf = 1ll << 60;
int n, w[N], m;
int id[N][N], tot, S, T;
int ecnt = 1, hd[M], cur[M], nt[M], vr[M];
LL cap[M];
void ad(int x, int y, LL w) {
nt[++ecnt] = hd[x], hd[x] = ecnt;
vr[ecnt] = y, cap[ecnt] = w;
}
void ad_e(int x, int y, LL w) {
ad(x, y, w), ad(y, x, 0);
}
int q[M], h, t, d[M];
bool bfs() {
for (int i = 1; i <= tot; ++i) d[i] = -1;
d[q[h = t = 1] = S] = 0;
while (h <= t) {
int x = q[h ++];
for (int i = hd[x],y; i; i = nt[i])
if (cap[i] && -1 == d[y = vr[i]])
d[q[++t] = y] = d[x] + 1;
}
return -1 != d[T];
}
LL dfs(int x, LL flw) {
if (x == T) return flw;
LL rst = flw;
for (int &i=cur[x],y; i; i = nt[i])
if (cap[i] && d[y = vr[i]] == d[x] + 1) {
LL tmp = dfs(y, min(cap[i], rst));
if(!tmp) d[y] = -1;
else cap[i] -= tmp, cap[i^1] += tmp, rst -= tmp;
if(!rst) break;
}
return flw - rst;
}
bool vis[M];
void dfs2 (int x) {
vis[x] = true;
for (int i = hd[x]; i; i = nt[i])
if(cap[i] && !vis[vr[i]])
dfs2(vr[i]);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++ i) for (int j = 0; j <= n; ++ j) id[i][j] = ++ tot;
S = ++tot, T = ++tot;
for (int i = 0; i < m; ++ i) {
int x, y, z; scanf("%d%d%d", &x, &y, &z);
w[x] += z, w[y] -= z;
for (int j = 0; j <= n-1; ++ j) ad_e(id[y][j], id[x][j+1], inf);
}
for (int i = 1; i <= n; ++ i) {
ad_e(S, id[i][0], inf), ad_e(id[i][n], T, inf);
for (int j = 0; j < n; ++ j) ad_e(id[i][j], id[i][j+1], (LL)w[i] * j + bignum);
}
while (bfs()) {
for (int i = 1; i <= tot; ++ i) cur[i] = hd[i];
int now = 0;
while ((now = dfs(S, inf)));
}
dfs2(S);
for (int i = 1; i <= n; ++ i) {
int ans = 0;
for (int j = 0; j < n; ++ j) if(vis[id[i][j]]) ans = j;
cout << ans << ' ';
}
return 0;
}
状压:
可以将 DAG 分层, 相邻两层的点权差为 1, 发现显然是可以做到的, 最后从 dp 状态逆推点权即可。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 18;
const LL inf = 1e18;
int n, m, e[N][N], in[N], lg[1 << N];
int Sin[1 << N];
LL Soutval[1 << N];
LL dp[1 << N];
int pre[1 << N];
#define lowb(x) (x&(-x))
int ans[N];
void giao(int x, int v) {
if(!x) return;
giao(pre[x], v + 1);
x = pre[x] ^ x;
while (x) {
ans[lg[lowb(x)]] = v;
x &= x-1;
}
}
int main()
{
scanf("%d%d", &n, &m);
int all = (1 << n) - 1;
for (int i = 0; i < m; ++ i) {
int x, y, z; scanf("%d%d%d", &x, &y, &z);
--x, --y;
e[x][y] = z;
in[y] |= 1 << x;
}
for (int i = 0; i < N; ++ i) lg[1 << i] = i;
for (int S = 1; S < (1 << n); ++ S) {
int x = lowb(S), lgx = lg[x];
Sin[S] = Sin[S ^ x] | in[lgx];
LL up = 0ll, down = 0ll;
for (int j = 0; j < n; ++j)
if((S >> j) & 1)
down += e[j][lgx];
else
up += e[lgx][j];
Soutval[S] = Soutval[S ^ x] + up - down;
dp[S] = inf;
}
for (int S = 0; S < (1 << n); ++ S) {
int T = all ^ S;
for (int nS = T; nS; nS = T & (nS - 1))
if((S & Sin[nS]) == Sin[nS]) {
if(dp[S | nS] > dp[S] + Soutval[S]) {
dp[S | nS] = dp[S] + Soutval[S];
pre[S | nS] = S;
}
}
}
giao(all, 0);
for (int i = 0; i < n; ++i) cout << ans[i] << ' ';
return 0;
}