题意
传送门 POJ 3422 Kaka’s Matrix Travels
题解
此类点或边带有权值和上界限制的最优化问题,恰好与费用流的模型非常相似。
使用点边转化的技巧,将格子 ( i , j ) (i,j) (i,j) 代表的节点拆为“入点” x x x 与“出点” x ′ x' x′。从每个 ( i , j ) (i,j) (i,j) 的 x x x 向 x ′ x' x′ 连一条容量为 1 1 1 费用为 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j] 的边,以及一条容量为 k − 1 k-1 k−1 费用为 0 0 0 的边,代表第一次经过这个点时可以把数取走,之后不再计算;每个节点的“出点”向网格中可以转移(向下或向右)点的“入点”连一条容量为 k k k 费用为 0 0 0 的边。以 ( 0 , 0 ) (0,0) (0,0) 的“入点”为源点, ( n − 1 , n − 1 ) (n-1,n-1) (n−1,n−1) 的“出点”为汇点求解最大费用流即可。总时间复杂度 O ( K N 4 ) O(KN^4) O(KN4)。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 55, maxv = 2 * maxn * maxn, maxe = 8 * maxn * maxn;
int N, K, num[maxn][maxn], id[maxn][maxn][2];
int tot, head[maxv], to[maxe], cap[maxe], cost[maxe], nxt[maxe];
int ds[maxv], rst[maxv], pre[maxv];
bool inq[maxv];
inline void add(int x, int y, int c, int z)
{
to[++tot] = y, cap[tot] = c, cost[tot] = z, nxt[tot] = head[x], head[x] = tot;
to[++tot] = x, cap[tot] = 0, cost[tot] = -z, nxt[tot] = head[y], head[y] = tot;
}
bool spfa(int s, int t)
{
memset(ds, 0xcf, sizeof(ds));
memset(inq, 0, sizeof(inq));
ds[s] = 0, rst[s] = inf;
queue<int> q;
q.push(s), inq[s] = 1;
while (q.size())
{
int x = q.front();
q.pop(), inq[x] = 0;
for (int i = head[x]; i; i = nxt[i])
{
int y = to[i], c = cap[i], z = cost[i];
if (!c)
continue;
if (ds[y] < ds[x] + z)
{
ds[y] = ds[x] + z, rst[y] = min(rst[x], c), pre[y] = i;
if (!inq[y])
q.push(y), inq[y] = 1;
}
}
}
return ds[t] != (int)0xcfcfcfcf;
}
int max_cost_flow(int s, int t)
{
int flow = 0, res = 0;
while (spfa(s, t))
{
int x = t, f = rst[t], z = ds[t];
while (x != s)
{
int i = pre[x];
cap[i] -= f, cap[i ^ 1] += f, x = to[i ^ 1];
}
flow += f, res += f * z;
}
return res;
}
int main()
{
scanf("%d%d", &N, &K);
tot = 1;
int bs = N * N;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
{
scanf("%d", &num[i][j]);
id[i][j][0] = i * N + j, id[i][j][1] = bs + i * N + j;
}
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
{
add(id[i][j][0], id[i][j][1], 1, num[i][j]);
add(id[i][j][0], id[i][j][1], K - 1, 0);
if (i + 1 < N)
add(id[i][j][1], id[i + 1][j][0], K, 0);
if (j + 1 < N)
add(id[i][j][1], id[i][j + 1][0], K, 0);
}
printf("%d\n", max_cost_flow(id[0][0][0], id[N - 1][N - 1][1]));
return 0;
}