洛谷 - P4014 分配问题(费用流/KM)

题目链接:点击查看

题目大意:给出n个工人和n个工作,每个人做每一个工作的效率都是不同的,问如何分配能让效率最低/最高

题目分析:最小费用最大流和最大费用最大流的模板题,直接套模板跑答案就行了,没有任何细节需要注意,就是两个模板组合在一起时会有很多函数的功能都是相同的,确切来说修改一下spfa的内部实现然后复制一份一模一样的就好了,一开始可以先把题目给出的效率矩阵储存下来,在跑费用流之前重新建边就好了,不然在跑完第一次费用流后的图已经变为残余网络了,需要重新建边再跑一遍费用流就好了

感觉这个题毕竟是二分图最大权匹配,用KM跑应该能更快一些吧

代码:

费用流:40ms

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
    
typedef long long LL;
    
const int inf=0x3f3f3f3f;
 
const int N=210;//点
 
const int M=N*N;//边
 
struct Edge
{
	int to,w,cost,next;
}edge[M];
 
int head[N],cnt,maze[N][N],n,st=N-1,ed=st-1;
 
void addedge(int u,int v,int w,int cost)
{
	edge[cnt].to=v;
	edge[cnt].w=w;
	edge[cnt].cost=cost;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	edge[cnt].to=u;
	edge[cnt].w=0;
	edge[cnt].cost=-cost;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
 
int d[N],incf[N],pre[N];
 
bool vis[N];
 
bool spfa1(int s,int t)
{
	memset(d,inf,sizeof(d));
	memset(vis,false,sizeof(vis));
    memset(pre,-1,sizeof(pre));
	queueq;
	q.push(s);
	vis[s]=true;
	incf[s]=inf;
	d[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			int w=edge[i].w;
			int cost=edge[i].cost;
			if(!w)
				continue;
			if(d[v]>d[u]+cost)
			{
				d[v]=d[u]+cost;
				pre[v]=i;
				incf[v]=min(incf[u],w);
				if(!vis[v])
				{
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	return pre[t]!=-1;
}

bool spfa2(int s,int t)
{
	memset(d,0xcf,sizeof(d));
	memset(vis,false,sizeof(vis));
    memset(pre,-1,sizeof(pre));
	queueq;
	q.push(s);
	vis[s]=true;
	incf[s]=inf;
	d[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			int w=edge[i].w;
			int cost=edge[i].cost;
			if(!w)
				continue;
			if(d[v]

KM:27ms

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
    
typedef long long LL;
    
const int inf=0x3f3f3f3f;
 
const int N=210;
 
int n;
 
int la[N],lb[N];//顶标
 
bool visa[N],visb[N];//访问标记
 
int maze[N][N];//边权
 
int match[N];//右部点匹配了哪一个左部点
 
int upd[N];
 
bool dfs(int x)
{
	visa[x]=true;//访问标记:x在交错树中
	for(int i=1;i<=n;i++)
	{
		if(!visb[i])
		{
			if(la[x]+lb[i]-maze[x][i]==0)//相等子图
			{
				visb[i]=true;//访问标记:y在交错树中
				if(!match[i]||dfs(match[i]))
				{
					match[i]=x;
					return true;
				}
			}
			else
				upd[i]=min(upd[i],la[x]+lb[i]-maze[x][i]);
		}
	}
	return false;
} 
 
int KM1()
{
	memset(match,0,sizeof(match));
	for(int i=1;i<=n;i++)
	{
		la[i]=-inf;
		lb[i]=0;
		for(int j=1;j<=n;j++)
			la[i]=max(la[i],maze[i][j]);
	}
	for(int i=1;i<=n;i++)
	{
		while(1)//直到左部点找到匹配
		{
			memset(visa,false,sizeof(visa));
			memset(visb,false,sizeof(visb));
			memset(upd,inf,sizeof(upd));
			if(dfs(i))
				break;
			int delta=inf;
			for(int j=1;j<=n;j++)
				if(!visb[j])
					delta=min(delta,upd[j]);
			for(int j=1;j<=n;j++)//修改顶标
			{
				if(visa[j])
					la[j]-=delta;
				if(visb[j])
					lb[j]+=delta;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=maze[match[i]][i];
	return ans;
}

int KM2()
{
	memset(match,0,sizeof(match));
	for(int i=1;i<=n;i++)
	{
		la[i]=-inf;
		lb[i]=0;
		for(int j=1;j<=n;j++)
			la[i]=max(la[i],maze[i][j]);
	}
	for(int i=1;i<=n;i++)
	{
		while(1)//直到左部点找到匹配
		{
			memset(visa,false,sizeof(visa));
			memset(visb,false,sizeof(visb));
			memset(upd,inf,sizeof(upd));
			if(dfs(i))
				break;
			int delta=inf;
			for(int j=1;j<=n;j++)
				if(!visb[j])
					delta=min(delta,upd[j]);
			for(int j=1;j<=n;j++)//修改顶标
			{
				if(visa[j])
					la[j]-=delta;
				if(visb[j])
					lb[j]+=delta;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=maze[match[i]][i];
	return ans;
}
 
int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++)
    	{
    		int val;
    		scanf("%d",&val);
    		maze[i][j]=-val;
		}
	printf("%d\n",-KM1());
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++)
    		maze[i][j]*=-1;
    printf("%d\n",KM2());
    
    
    
    
    
    
 
        
        
        
         
        
    return 0;
}

 

你可能感兴趣的:(图论,网络流24题,费用流,KM算法)