P2457题解
Aisaka_Taiga · · 题解
基本思路:KM 算法求最优匹配
如果你不会 KM 算法请点这里
先来定义一些数组方便下面表达:
int n,m,mp[N][N];//mp存放输入的矩阵
int sum[N];//sum存放每一种货物重量的总和
int va[N],vb[N]//va,vb标记每一次dfs左右参与的点
int w[N][N];//w存放把i其余所有货物搬到仓库j的代价
int la[N],lb[N]//la,lb分别为左右标杆
int mi[N];//mi为右部点匹配的点
int vis[N];//vis标记当前dfs每一个点最少下降多少可以有其他的选择
题目给出的输入第
这题让着求最小代价,和一般的不太一样怎么办?
只要在处理
code:
#include<bits/stdc++.h>
#define INF 0x7fffffff
#define N 510
using namespace std;
int n,m,mp[N][N],sum[N];//mp存放输入的矩阵,sum存放每一种货物重量的总和
int va[N],vb[N],w[N][N];//va,vb标记每一次dfs左右参与的点,w存放把i其余所有货物搬到仓库j的代价
int la[N],lb[N],mi[N],vis[N];//la,lb分别为左右标杆,mi为右部点匹配的点,
inline int read()
{
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9')
{ if(ch=='-') w=-1; ch=getchar();}
while(ch>='0'&&ch<='9')
{ s=s*10+ch-'0'; ch=getchar();}
return s*w;
}
inline int dfs(int x)//匈牙利算法
{
va[x]=1;//标记参与本次dfs
for(int i=1;i<=n;i++)//枚举每一个右部的点
if(w[x][i])//如果当前两点之间存在边
if(!vb[i])//vb没有标记已经搜过了
{
if(la[x]+lb[i]==w[x][i])//如果当前左右标杆加起来等于边权
{
vb[i]=1;//标记可以
if(!mi[i]||dfs(mi[i]))//如果当前右部点没有与之相匹配的点或者与之相匹配的点可以与其他点匹配
{
mi[i]=x;//当前左部点与右部点匹配
return 1;//返回匹配成功
}
}
else vis[i]=min(vis[i],la[x]+lb[i]-w[x][i]);//如果不等于边权,求出下降最小可以有其他解的值
}
return 0;//返回匹配失败
}
int KM()//KM算法板子
{
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],w[i][j]);//给左部标杆赋初值
}
for(int i=1;i<=n;i++)//匹配每一个左部点
{
while(1)
{
memset(va,0,sizeof(va));//每次都要清空va,vb
memset(vb,0,sizeof(vb));
for(int j=1;j<=n;j++)
vis[j]=INF;//赋初值,vis表示每一个点最少下降多少可以有另一种选择
if(dfs(i))break;//如果当前点匹配成功直接退出
int d=INF;//赋初值
for(int j=1;j<=n;j++)
d=min(d,vis[j]);//取每一个vis的最小值
for(int j=1;j<=n;j++)
{
if(va[j])la[j]-=d;//左部点减d
if(vb[j])lb[j]+=d;//右部点加d
}
}
}
int ans=0;//ans表示答案
for(int i=1;i<=n;i++)//枚举每一个右部点
ans+=w[mi[i]][i];//累加所选的边权
return ans;//返回答案
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x;
x=read();
mp[i][j]=x;//存图
sum[j]+=x;//累加一种货物的重量总和
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
w[i][j]=-(sum[i]-mp[j][i]);//转换一下求出要把所有货物放到最此仓库的代价,取相反数求最大边权匹配
printf("%d",-KM());//输出的时候别忘了取反
return 0;
}//by wwwaax