POJ 3422 Kaka's Matrix Travels 最小费用最大流

题意:给一个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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值