题意:给一个n*n的矩阵,可以走k次,只能往右或往下走,每次走到一个点取走这个点的数,求取走数的和的最大值
思路:要是按照我之前的思路,第一个想到的不是dfs就是dp,谁知道这种题居然可以用网络流来做,建图方式对我这个水平来说可以说是非常牛逼了。由于答案要求最大费用,故可以先把边权取负,求出最小费用,最后再取反。对于每个点拆成两个点,由于一个点可以表示为走过或者没走过,故可以建两条边,一条边权-t,容量为1,表示这个点没走过;另一条边容量为无穷大,边权为0,表示这个点已经走过了,数被取走边权变为0。对于每个点,在往右和往下之间连一条边,边权为0,容量无穷大。在源点和左上角,右下角和汇点之间连一条容量为k,表示能走k次,边权为0的边。之后求出源点到汇点的最小费用即可。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 5005;
const int inf = 0x3f3f3f3f;
int m, n, k, vis[maxn], flow[maxn], dis[maxn], N;
struct edge {
int rev, to, c, w;
edge(int rev = 0, int to = 0, int c = 0, int w = 0) : rev(rev), to(to), c(c), w(w) {}
};
struct node {
int id, pre;
node(int id = -1, int pre = -1) : id(id), pre(pre) {}
}path[maxn];
vector<edge> g[maxn];
void addedge(int u, int v, int c, int w)
{
g[u].push_back(edge(g[v].size(), v, c, w));
g[v].push_back(edge(g[u].size()-1, u, 0, -w));
}
int spfa(int s, int t)
{
memset(dis, inf, sizeof(dis));
memset(vis, 0, sizeof(vis));
memset(flow, inf, sizeof(flow));
queue<int> q;
q.push(s); dis[s] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
vis[u] = 0;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i].to, di = g[u][i].w, c = g[u][i].c;
if (c && dis[v] > dis[u] + di) {
dis[v] = dis[u] + di;
path[v].id = i;
path[v].pre = u;
flow[v] = min(flow[u], c);
if (!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
return flow[t] != flow[s];
}
void solve(int s, int t)
{
int maxflow = 0, mincost = 0, id, now, pre;
while (spfa(s, t)) {
maxflow += flow[t];
mincost += flow[t]*dis[t];
now = t;
while (now != s) {
pre = path[now].pre, id = path[now].id;
edge &e = g[pre][id];
e.c -= flow[t];
g[e.to][e.rev].c += flow[t];
now = pre;
}
}
cout << -mincost << endl;
}
int main()
{
while (cin >> n) {
cin >> k;
int cnt = 0;
addedge(0, 1, k, 0);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
int t;
cin >> t;
cnt++;
addedge(2*cnt-1, 2*cnt, 1, -t);
addedge(2*cnt-1, 2*cnt, inf, 0);
if ((cnt + 1) % n != 1)
addedge(2*cnt, 2*(cnt+1)-1, inf, 0);
if (cnt + n <= n*n)
addedge(2*cnt, 2*(cnt+n)-1, inf, 0);
}
addedge(2*n*n, 2*n*n+1, k, 0);
solve(0, 2*n*n+1);
}
return 0;
}