UVA 11383 Golden Tiger Claw
带权二分图最大完美匹配,深入理解KM算法
题意
题意:给一个n*n的矩阵,每个格子中有正整数w[i][j],试为每行和每列分别确定一个数字row[i]和col[i],使得任意格子w[i][j]<=row[i]+col[j]恒成立。先输row,再输出col,再输出全部总和(总和应尽量小)。
思路
利用KM算法中的l(x)+l(y)>=w(x,y)。算法结束后所有标顶之和是最小的。即所求答案。
见训练指南P351
代码
#include<bits/stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int MAXN=507;
const int oo=0x3f3f3f3f;
typedef long long LL;
int w[MAXN][MAXN],x[MAXN],slack[MAXN],y[MAXN];
int prev_x[MAXN],prev_y[MAXN],son_y[MAXN],par[MAXN];
int lx,ly,pop;
///w表示带权图
///pop表示一侧点数
///返回最大完美匹配权值
///son_y表示匹配方案
void adjust(int v)
{
son_y[v]=prev_y[v];
if(prev_x[son_y[v]]!=-2)
adjust(prev_x[son_y[v]]);
}
bool fin_d(int v)
{
for(int i=1; i<=pop; i++)
{
if(prev_y[i]==-1)
{
if(slack[i]>x[v]+y[i]-w[v][i])
{
slack[i]=x[v]+y[i]-w[v][i];
par[i]=v;
}
if(x[v]+y[i]==w[v][i])
{
prev_y[i]=v;
if(son_y[i]==-1)
{
adjust(i);
return true;
}
if(prev_x[son_y[i]]!=-1) continue;
prev_x[son_y[i]]=i;
if(fin_d(son_y[i])) return true;
}
}
}
return false;
}
int KM()
{
int m=0;
for(int i=1; i<=pop; i++)
{
son_y[i]=-1;
y[i]=0;
}
for(int i=1; i<=pop; i++)
{
x[i]=0;
for(int j=1; j<=pop; j++)
x[i]=max(x[i],w[i][j]);
}
bool flag;
for(int i=1; i<=pop; i++)
{
for(int j=1; j<=pop; j++)
{
prev_x[j]=prev_y[j]=-1;
slack[j]=oo;
}
prev_x[i]=-2;
if(fin_d(i)) continue;
flag=false;
while(!flag)
{
m=oo;
for(int j=1; j<=pop; j++)
{
if(prev_y[j]==-1)
m=min(m,slack[j]);
}
for(int j=1; j<=pop; j++)
{
if(prev_x[j]!=-1)
x[j]-=m;
if(prev_y[j]!=-1)
y[j]+=m;
else slack[j]-=m;
}
for(int j=1; j<=pop; j++)
{
if(prev_y[j]==-1&&!slack[j])
{
prev_y[j]=par[j];
if(son_y[j]==-1)
{
adjust(j);
flag=true;
break;
}
prev_x[son_y[j]]=j;
if(fin_d(son_y[j]))
{
flag=true;
break;
}
}
}
}
}
int ans=0;
for(int i=1; i<=pop; i++)
ans+=w[son_y[i]][i];
return ans;
}
int main()
{
int n;
while(scanf("%d",&n)==1)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
{
scanf("%d",&w[i][j]);
}
pop=n;
int res=KM();
for(int i=1;i<=n;i++) printf("%d%c",x[i],i==n?'\n':' ');
for(int i=1;i<=n;i++) printf("%d%c",y[i],i==n?'\n':' ');
printf("%d\n",res);
}
return 0;
}