「网络流24题」 18. 分配问题

「网络流24题」 18. 分配问题

<题目链接>


费用流其实是可以做这题的。

但这篇主要说一下二分图最佳完美匹配——Kuhn-Munkres(KM)算法。

工作是X部,费用是Y部,边权为工作效益。

通过X部减去/Y部增加增广路上的松弛量,修改「顶标」(又称标杆)。

初始顶标:X部点:最大权出边的边权;Y部点:0。

跑出来后,所有顶标和是最大效益。

所有边取负,跑出来的和的相反数是最小效益。

具体请看此篇题解

KM写法

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN=210,MAXM=10010,INF=0x3f3f3f3f;
bool vis[MAXN];
int n,cnt,ans,head[MAXN],match[MAXN],mark[MAXN];
struct edge
{
	int nxt,to,w;
}e[MAXM];
void AddEdge(int u,int v,int w)
{
	e[++cnt].nxt=head[u];
	e[cnt].to=v;
	e[cnt].w=w;
	head[u]=cnt;
}
void Init(bool flag)
{
	ans=0;
	memset(match,0,sizeof match);
	memset(mark,0xb0,sizeof mark);
	for(int u=1;u<=n;++u)
		for(int i=head[u];i;i=e[i].nxt)
			mark[u]=max(mark[u],flag ? e[i].w=-e[i].w : e[i].w),mark[e[i].to]=0;
}
bool DFS(int u)
{
	vis[u]=1;
	for(int i=head[u],v;i;i=e[i].nxt)
		if(!vis[v=e[i].to] && e[i].w==mark[u]+mark[v])
		{
			vis[v]=1;
			if(!match[v] || DFS(match[v]))
			{
				match[v]=u;
				return 1;
			}
		}
	return 0;
}
void Update(void)
{
	int d=INF;
	for(int u=1;u<=n;++u)
		if(vis[u])
			for(int i=head[u],v;i;i=e[i].nxt)
				if(!vis[v=e[i].to])
					d=min(d,mark[u]+mark[v]-e[i].w);
	for(int i=1,j;i<=n;++i)
	{
		if(vis[i])
			mark[i]-=d;
		if(vis[j=i+n])
			mark[j]+=d;
	}
}
void KM(bool flag)
{
	Init(flag);
	for(int i=1;i<=n;++i)
		while(1)
		{
			memset(vis,0,sizeof vis);
			if(DFS(i))
				break;
			Update();
		}
	for(int i=1;i<=n;++i)
		ans+=mark[i]+mark[i+n];
	printf("%d\n",flag ? ans : -ans);
}
int main(int argc,char *argv[])
{
	scanf("%d",&n);
	memset(mark,0xb0,sizeof mark);
	for(int i=1;i<=n;++i)
		for(int j=1,w;j<=n;++j)
		{
			scanf("%d",&w);
			AddEdge(i,j+n,-w);
		}
	KM(0);
	KM(1);
	return 0;
}

MCMF写法

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN=210,MAXM=10210,INF=0x3f3f3f3f;
bool exist[MAXN];
int n,S,T,cnt,ans,head[MAXN],dis[MAXN],flow[MAXN],pre[MAXN],pre_e[MAXN],c[MAXN][MAXN];
struct edge
{
    int nxt,to,w,p;
}e[MAXM];
void AddEdge(int u,int v,int w,int p)
{
    e[++cnt].nxt=head[u];
    e[cnt].to=v;
    e[cnt].w=w;
    e[cnt].p=p;
    head[u]=cnt;
}
void AddEdges(int u,int v,int w,int p)
{
    AddEdge(u,v,w,p);
    AddEdge(v,u,0,-p);
}
void Init(bool flag)
{
    cnt=ans=0;
    memset(head,0,sizeof head);
    for(int i=1;i<=n;++i)
    {
        AddEdges(S,i,1,0);
        AddEdges(i+n,T,1,0);
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            AddEdges(i,j+n,1,flag ? c[i][j] : -c[i][j]);
}
bool SPFA(void)
{
    queue<int> q;
    memset(exist,0,sizeof exist);
    memset(dis,0x3f,sizeof dis);
    memset(flow,0x3f,sizeof flow);
    memset(pre,0,sizeof pre);
    memset(pre_e,0,sizeof pre_e);
    q.push(S);
    exist[S]=dis[S]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        exist[u]=0;
        for(int i=head[u],v;i;i=e[i].nxt)
            if(e[i].w && dis[v=e[i].to]>dis[u]+e[i].p)
            {
                dis[v]=dis[u]+e[i].p;
                flow[v]=min(flow[u],e[i].w);
                pre[v]=u,pre_e[v]=i;
                if(!exist[v])
                {
                    q.push(v);
                    exist[v]=1;
                }
            }
    }
    return dis[T]!=INF;
}
void MCMF(bool flag)
{
    Init(flag);
    while(SPFA())
        for(int i=T,t;i!=S;i=pre[i])
        {
            e[t=pre_e[i]].w-=flow[T];
            e[((t-1)^1)+1].w+=flow[T];
            ans+=e[t].p*flow[T];
        }
    printf("%d\n",flag ? ans : -ans);
}
int main(int argc,char *argv[])
{
    scanf("%d",&n);
    T=(n<<1)+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            scanf("%d",&c[i][j]);
    MCMF(1);
    MCMF(0);
    return 0;
}

谢谢阅读

posted @ 2018-01-08 13:52  Capella  阅读(347)  评论(0编辑  收藏  举报

谢谢光临