poj 3422
题意:给定 $n^2$的矩阵,含有元素值,每次从最左上角开始出发,每次向右或者向下一格。终点是右下角, 每经过一个格子,获取它的值,并把该格子的值变成$0$.问经过$k$次从左上角到右下角。能得到的数值和最大多少。
把走几次看成容量,数值和看出权值。$(w=w_0,cap=c_0)$表示权值$w_0$, 容量$c_0$
本题如果格子值不会变,那么直接点转边,入点到出点连边$(w=a_{i,j},cap=k)$,然后在网格图中连边$(w=0,cap=k)$,然后求最大费用最大流。
但是这里格子值会变化,所以我们要限制多次走这个点获得多次收益的情况。
我们将入点到出点的边拆边,拆成$(w=a_{i,j}, cap=1), (w=0, cap=k-1)$两条边,然后这样做就可以解决问题了,由于最大费用基于最大流,所以一定能完成不超过$k$次(全部数取完后也可以随便选择路径走,等效)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cmath>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db long double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
const int INF = 2147483647ll;
struct edge {
int u, v, cap, w, nxt;
} ed[500000 + 5];
int n, k, hd[10000 + 5], en, s, t, ans;
int incf[10000 + 5], dis[10000 + 5], pre[10000 + 5], vis[10000 + 5];
int getIDbyPos(int x, int y) {return (x - 1) * n + y;}
void ins_c(int u, int v, int cap, int w) {
ed[++en] = (edge){u, v, cap, w, hd[u]}, hd[u] = en;
ed[++en] = (edge){v, u, 0, -w, hd[v]}, hd[v] = en;
}
queue<int > q;
bool spfa() {
for (int i = 0; i <= n * n * 2; ++i) incf[i] = INF, dis[i] = -INF, pre[i] = -1, vis[i] = 0;
vis[s] = 1, dis[s] = 0, q.push(s);
while (!q.empty()) {
int u = q.front(); q.pop();
vis[u] = 0;
for (int i = hd[u]; i >= 0; i = ed[i].nxt) {
edge &e = ed[i];
if (e.cap && dis[u] != -INF && dis[e.v] < dis[u] + e.w) {
dis[e.v] = dis[u] + e.w;
incf[e.v] = min(incf[u], e.cap);
pre[e.v] = i;
if (!vis[e.v]) vis[e.v] = 1, q.push(e.v);
}
}
}
//for (int i = 1; i <= n * n * 2; ++i) printf("%d ", dis[i]); puts("");
return dis[t] != -INF;
}
void update() {
int now = pre[t];
while (now != -1) {
ed[now].cap -= incf[t], ed[now ^ 1].cap += incf[t];
now = pre[ed[now].u];
}
ans += incf[t] * dis[t];
}
void clean() {
ans = 0, en = -1, ms(hd, -1);
}
int solve() {
clean();
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i)
for (int x, j = 1; j <= n; ++j) {
scanf("%d", &x);
ins_c(getIDbyPos(i, j), getIDbyPos(i, j) + n * n, 1, x);
ins_c(getIDbyPos(i, j), getIDbyPos(i, j) + n * n, k - 1, 0);
if (i < n) ins_c(getIDbyPos(i, j) + n * n, getIDbyPos(i + 1, j), k, 0);
if (j < n) ins_c(getIDbyPos(i, j) + n * n, getIDbyPos(i, j + 1), k, 0);
}
s = 1, t = 2 * n * n;
while (spfa()) update();
cout << ans;
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}