网络流24题——分配问题 luogu 4014

题目链接:这里

本题是一个典型的费用流问题,可以作为费用流建图模板使用

首先看到,每个人只能做一件工作,每件工作只能做一次,一个人做某件工作有一定的收益

那么我们建立一个超级源点st和超级终点ed,然后由源点向所有人连边,容量为1,费用为0

接着由工作向汇点连边,容量为1费用为0

上面满足了每个人只做一件工作且每件工作只做一次的要求

最后由人向工作连边,容量为1费用为收益,跑一遍费用流即为最小收益

然后把人向工作连边的边权取负值,再跑一遍费用流,此时最小费用的相反数即为最大收益

这道题向我们提示:最大费用流也是可以跑的,只是将所有费用取反后跑最小费用流即可

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int INF=0x3f3f3f3f;
struct Edge
{
	int next;
	int to;
	int val;
	int pri;
}edge[20005];
int head[255];
int dis[255];
int pre[255];
int fa[255];
int lim[255];
bool used[255];
int a[255][255];
int cnt=1;
int n;
int st,ed;
void init()
{
	memset(head,-1,sizeof(head));
	memset(edge,0,sizeof(edge));
	cnt=1;
}
void add(int l,int r,int w,int v)
{
	edge[cnt].next=head[l];
	edge[cnt].to=r;
	edge[cnt].val=w;
	edge[cnt].pri=v;
	head[l]=cnt++;
}
int ide(int x)
{
	return (x&1)?x+1:x-1;
}
bool spfa()
{
	memset(dis,0x3f,sizeof(dis));
	memset(lim,0,sizeof(lim));
	memset(used,0,sizeof(used));
	dis[st]=0;
	lim[st]=INF;
	pre[ed]=-1;
	used[st]=1;
	queue <int> M;
	M.push(st);
	while(!M.empty())
	{
		int u=M.front();
		M.pop();
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int to=edge[i].to;
			if(edge[i].val&&dis[to]>dis[u]+edge[i].pri)
			{
				dis[to]=dis[u]+edge[i].pri;
				lim[to]=min(lim[u],edge[i].val);
				pre[to]=i;
				fa[to]=u;
				if(!used[to])used[to]=1,M.push(to);
			}
		}
		used[u]=0;
	}
	return pre[ed]!=-1;
}
int EK()
{
	int maxw=0,minv=0;
	while(spfa())
	{
		minv+=dis[ed]*lim[ed];
		maxw+=lim[ed];
		int temp=ed;
		while(temp!=st)
		{
			edge[pre[temp]].val-=lim[ed];
			edge[ide(pre[temp])].val+=lim[ed];
			temp=fa[temp];
		}
	}
	return minv;
}
int main()
{
	scanf("%d",&n);
	init();
	for(int i=2;i<=n+1;i++)
	{
		for(int j=n+2;j<=2*n+1;j++)
		{
			int x;
			scanf("%d",&x);
			add(i,j,1,x);
			add(j,i,0,-x);
			a[i-1][j-n-1]=x;
		}
	}
	st=1,ed=2*n+2;
	for(int i=2;i<=n+1;i++)add(st,i,1,0),add(i,st,0,0);
	for(int i=n+2;i<=2*n+1;i++)add(i,ed,1,0),add(ed,i,0,0);
	printf("%d\n",EK());
	init();
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			add(i+1,j+n+1,1,-a[i][j]);
			add(j+n+1,i+1,0,a[i][j]);
		}
	}
	st=1,ed=2*n+2;
	for(int i=2;i<=n+1;i++)add(st,i,1,0),add(i,st,0,0);
	for(int i=n+2;i<=2*n+1;i++)add(i,ed,1,0),add(ed,i,0,0);
	printf("%d\n",-EK());
	return 0;
}

  

posted @ 2019-04-24 16:20  lleozhang  Views(145)  Comments(0Edit  收藏  举报
levels of contents