poj 3422 费用流

感觉网络流的问题 大部分都是考建图,  建图的时候又经常拆点来限流。这道题目建图比较简单,很适合学习用~~~

#include <stdio.h>   
#include <iostream>
#include <string.h>   
#include <cmath>
#include <algorithm>   
#define fr(i,s,n) for(int i=s;i<n;++i)
#define _fr(i,n,s) for(int i=n-1;i>=s;--i)
#define fi freopen("in.txt","r",stdin)
#define cl(a) memset(a,0,sizeof(a))
using namespace std;
typedef long long ll; typedef double dl;


const int inf = 0xffffff;
#define M 200001
#define maxx 2000
class Mcmf{
public:
	struct T{
		int from, to, val;
		int next, cost;
	}edge[M];
	int len;
	int visit[M], pre[M], dist[M], que[M], vis[M], pos[M];
	void init(){
		memset(vis,-1,sizeof(vis));
		len=0;
	}
	void add(int from, int to, int val, int cost)  
	{
		edge[len].from = from,edge[len].to = to, edge[len].val = val, edge[len].cost = cost;
		edge[len].next = vis[from], vis[from] = len++;
		edge[len].from = to, edge[len].to = from, edge[len].val = 0, edge[len].cost = -cost;
		edge[len].next = vis[to], vis[to] = len++;
	}
	int SPFA(int s, int t, int n)
	{
		int i, to, k;
		for (i = 0; i <= n; i++){
			pre[i] = -1, visit[i] = 0;
		}
		int head, tail;
		head = tail = 0;
		for (i = 0; i <= n; i++)
			dist[i] = -1;//求最小费用时这里得改成正无穷
		que[tail++] = s, pre[s] = s, dist[s] = 0, visit[s] = 1;
		while (head != tail){
			int from = que[head++];
			visit[from] = 0;
			for (k = vis[from]; k != -1; k = edge[k].next){
				to = edge[k].to;
				if (edge[k].val > 0 && dist[from]+edge[k].cost > dist[to]){//最大费用,求最小费用时这里的改符号
					dist[to] = dist[from] + edge[k].cost;
					pre[to] = from;
					pos[to] = k;
					if (!visit[to]){
						visit[to] = 1;
						que[tail++] = to;
					}
				}
			}
		}
		if (pre[t] != -1 && dist[t] >-1)//求最小费用时这里改成dit[t] < 正无穷 
			return 1;
		return 0;
	}
	int mnCostFlow(int s, int t, int n)
	{
		if (s == t){
			//这里具体情况具体分析,如果有附加s跟t就不用,如果用数据中的点作为s跟t就得考虑
			//直接返回某个值。否则SPFA里的队列会死循环。
		}
		int flow = 0, cost = 0;
		while (SPFA(s, t, n)){
			int from, mn = inf;
			for (from = t; from != s; from = pre[from])
				mn = min(mn, edge[pos[from]].val);
			flow += mn;
			cost += dist[t] * mn;
			for (from = t; from != s; from = pre[from]){
				edge[pos[from]].val -= mn;
				edge[pos[from]^1].val += mn;
			}
		}
		return cost;
	}

}mcf;

int N,K;
int map[1010][1010];
void build (){
	int n = N*N;
	int x;
	fr(i , 0 ,N){
		fr(j , 0, N){
			x = i * N + j;
			mcf.add(x ,x+n ,1 ,map[i][j] );
			mcf.add(x , x+n, K, 0);
			if ( i+1< N) mcf.add( x + n, x +N, inf ,0);
			if (j+1<N) mcf.add(x+n,x+1,inf,0);
		}
	}
	int s = n<<1,t = s+1;
	mcf.add(s , 0, K,0);
	mcf.add(s-1 ,t, K, 0);
	int ans =  mcf.mnCostFlow(s,t,t+1);
	printf("%d\n",ans);
}
int main(){
	while(scanf("%d%d",&N,&K)!=EOF){
		cl(map);
		mcf.init();
		fr(i,0,N)
			fr(k,0,N){
				scanf("%d",&map[i][k]);
		}
		build();
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值