# Description
Luogu 传送门
# Solution
- 最大费用最大流
 
虽然叫方格取数加强版,但是感觉跟方格取数没半毛钱关系(
我们还是要从当前点向下,向右连边。
观察题意,一个点被走过之后权值变为 0,但是还可以继续走,所以我们可以把一个点拆成入点和出点,然后从入点向出点连两条边。
建一条流量为 1,费用为 的边。
再建一条流量为 ,费用为 0 的边。
表示第一次走可以获得 的收益,但只能走一次,后面再走到当前点时没有收益。
从当前点向下方和右方连一条流量为 ,费用为 0 的边,仅表示一种联通关系。
超级源点和超级汇点可建可不建,直接用 的入点当源点, 的出点当汇点即可。
# Code
#include <bits/stdc++.h> | |
#define P(i, j, t) ((i - 1) * n + j + t * n * n) | |
using namespace std;  | |
namespace IO{  | |
inline int read(){  | |
int x = 0;  | |
char ch = getchar();  | |
while(!isdigit(ch)) ch = getchar();  | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();  | |
return x;  | |
    } | |
template <typename T> inline void write(T x){  | |
if(x > 9) write(x / 10);  | |
putchar(x % 10 + '0');  | |
    } | |
} | |
using namespace IO;  | |
const int N = 5e3 + 10;  | |
const int M = 2e5 + 10;  | |
const int INF = 2e9;  | |
int n, k, ans, s, t;  | |
struct node{  | |
int v, w, cost, nxt;  | |
}edge[M];  | |
int head[N], tot = 1;  | |
inline void add(int x, int y, int z, int cost){  | |
edge[++tot] = (node){y, z, cost, head[x]};  | |
head[x] = tot;  | |
} | |
inline void Add(int x, int y, int z, int cost){  | |
add(x, y, z, cost), add(y, x, 0, -cost);  | |
} | |
int dis[N], f[N], p[N];  | |
bool vis[N];  | |
inline bool spfa(){  | |
queue <int> q;  | |
memset(vis, 0, sizeof(vis));  | |
memset(dis, 128, sizeof(dis));  | |
q.push(s), vis[s] = 1, dis[s] = 0, f[s] = INF;  | |
while(!q.empty()){  | |
int x = q.front();  | |
q.pop();  | |
vis[x] = 0;  | |
for(int i = head[x]; i; i = edge[i].nxt){  | |
if(!edge[i].w) continue;  | |
int y = edge[i].v;  | |
if(dis[y] < dis[x] + edge[i].cost){  | |
dis[y] = dis[x] + edge[i].cost;  | |
f[y] = min(f[x], edge[i].w), p[y] = i;  | |
if(!vis[y]) vis[y] = 1, q.push(y);  | |
            } | |
        } | |
    } | |
return dis[t] > -INF;  | |
} | |
inline void solve(){  | |
while(spfa()){  | |
int x = t;  | |
while(x != s){  | |
int i = p[x];  | |
edge[i].w -= f[t], edge[i ^ 1].w += f[t];  | |
x = edge[i ^ 1].v;  | |
        } | |
ans += dis[t] * f[t];  | |
    } | |
} | |
int main(){  | |
n = read(), k = read();  | |
s = 1, t = P(n, n, 1);  | |
for(int i = 1; i <= n; ++i)  | |
for(int j = 1; j <= n; ++j){  | |
int w = read();  | |
Add(P(i, j, 0), P(i, j, 1), 1, w);  | |
Add(P(i, j, 0), P(i, j, 1), k - 1, 0);  | |
if(i < n) Add(P(i, j, 1), P(i + 1, j, 0), k, 0);  | |
if(j < n) Add(P(i, j, 1), P(i, j + 1, 0), k, 0);  | |
        } | |
solve();  | |
write(ans), puts("");  | |
return 0;  | |
} |