poj3422 最小费用最大流

记得以前做过这样类似的题,因为那时候求的是来回的最大值,直接使用的dp,而且对费用流并不是很清楚,然后又看到了这道题。。。

对点进行拆分建图,一个点拆为两个点a和b,在a和b之间建一条花费为输入值容量为1的边,然后再建一条花费为0容量为k-1的边,对b点对于其右边和下边都建立一条容量为k花费为0的边,加入超级源点和汇点,花费为0容量为k,由此套模板就可以了!

下面是代码:

/
// File Name: 2195.cpp
// Author: wang
// mail: 1062239606@qq.com
// Created Time: 2013/10/25 19:28:26
/
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <cstring>
#include <cmath>

#include <algorithm>
#include<iostream>
#include<queue>
#include <map>
using namespace std;
typedef long long ll;
#define INF (INT_MAX/10)
#define SQR(x) ((x)*(x))
#define rep(i, n) for (int i=0; i<(n); ++i)
#define repf(i, a, b) for (int i=(a); i<=(b); ++i)
#define repd(i, a, b) for (int i=(a); i>=(b); --i)
#define clr(ar,val) memset(ar, val, sizeof(ar))
#define inf 100000000
#define N 5010
class match{
public:
	//s和t要赋值的,并且花费一个为正一个为负刚好抵消的
	int s,t;//源点和结束点的,开始为1 的
	struct node{
		int y,cost,cap,pre;
	};
	node a[N*1000];
	int dis[N];
	bool vis[N];
	int point[N];
	int len,pre[N];
	void init()
	{
		len=0;
		memset(pre,-1,sizeof(pre));
	}
	void addpage(int x,int y,int cap,int cost)
	{
         a[len].y=y;
		 a[len].pre=pre[x];
		 a[len].cap=cap;
		 a[len].cost=cost;
		 pre[x]=len++;
	}
	bool spfa()
	{
        repf(i,1,t) vis[i]=false,dis[i]=inf;
		vis[s]=true; dis[s]=0;
		queue<int>q;
		q.push(s);
		while(!q.empty())
		{
			int x=q.front(); q.pop();
			vis[x]=false;
			for(int i=pre[x]; i!=-1; i=a[i].pre)
			{
				int y=a[i].y;
				if(a[i].cap && dis[y]>dis[x]+a[i].cost)
				{
                    point[y]=i;//记录边的
					dis[y]=dis[x]+a[i].cost;
					if(vis[y]==false)
					{
						vis[y]=true;
						q.push(y);
					}
				}
			}
		}
		if(dis[t]!=inf) return true;
		else return false;
	}
	int fond()
	{
		int ans=0;
		while(spfa())
		{
			int Min=INT_MAX;
            for(int i=t; i!=s; i=a[point[i]^1].y)//最小的容量,进行扩展的
				Min=min(Min,a[point[i]].cap);
			for(int i=t; i!=s; i=a[point[i]^1].y)
			{
				a[point[i]].cap-=Min;
				a[point[i]^1].cap+=Min;
				ans+=Min*a[point[i]].cost;
			}
		}
		return -ans;
	}
};
match sa;
int a[55][55];
int n,m;
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
        repf(i,1,n)
			repf(j,1,n)
			scanf("%d",&a[i][j]);
		sa.init();
		sa.s=0; sa.t=2*n*n+1;
		repf(i,1,n)
			repf(j,1,n)
			{
				int k=(i-1)*n+j;//2*k,2*k-1
                sa.addpage(2*k-1,2*k,1,-a[i][j]);
				sa.addpage(2*k,2*k-1,0,a[i][j]);
				sa.addpage(2*k-1,2*k,m-1,0);
				sa.addpage(2*k,2*k-1,0,0);
				if(i<n)
					sa.addpage(2*k,2*(i*n+j)-1,m,0),
						sa.addpage(2*(i*n+j)-1,2*k,0,0);
				if(j<n)
					sa.addpage(2*k,2*(i*n-n+j+1)-1,m,0),
						sa.addpage(2*(i*n-n+j+1)-1,2*k,0,0);
			}
	   sa.addpage(sa.s,1,m,0);
	   sa.addpage(1,sa.s,0,0);
	   sa.addpage(2*n*n,2*n*n+1,m,0);
	   sa.addpage(2*n*n+1,2*n*n,0,0);
	   printf("%d\n",sa.fond());
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淡定的小Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值