思路:
原版KM算法求的是最大权值完美匹配,这题要求的是最小权值完美匹配,第一种思路,把边取负,跑最大权值km算法,得到的答案取负即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 103;
const int inf = 0x3f3f3f3f;
int w[maxn][maxn],lx[maxn],ly[maxn],slack[maxn],link[maxn],visx[maxn],visy[maxn],arr[maxn][maxn];
int n,m;
bool dfs(int u)
{
visx[u] = 1;
for(int v = 1;v<=n;v++)
{
if(!visy[v])
{
int gap = lx[u]+ly[v]-w[u][v];
if(gap==0)
{
visy[v] = 1;
if(link[v]==-1||dfs(link[v]))
{
link[v] = u;
return true;
}
}
else
{
slack[v] = min(slack[v],gap);
}
}
}
return false;
}
void update()
{
int d = inf;
for(int i = 1;i<=n;i++)
{
if(!visy[i])
{
d = min(d,slack[i]);
}
}
for(int i = 1;i<=n;i++)
{
if(visx[i])
lx[i] -= d;
if(visy[i])
ly[i] += d;
}
}
int km()
{
memset(link,-1,sizeof(link));
for(int i = 1;i<=n;i++)
{
ly[i] = 0,lx[i] = -inf;
for(int j = 1;j<=n;j++)
{
//cout<<w[i][j]<<endl;
lx[i] = max(lx[i],w[i][j]);
}
//cout<<lx[i]<<endl;
}
for(int i = 1;i<=n;i++)
{
memset(slack,inf,sizeof(slack));
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(dfs(i))
break;
else
update();
}
}
int ans = 0;
for(int i = 1;i<=n;i++)
{
ans += lx[i]+ly[i];
}
return ans;
}
struct node
{
int x,y;
}px[maxn],py[maxn];
int xl,yl;
void input()
{
xl = yl = 1;
for(int i = 0;i<n;i++)
{
getchar();
for(int j = 0;j<m;j++)
{
scanf("%c",&arr[i][j]);
}
}
for(int i = 0;i<n;i++)
{
for(int j = 0;j<m;j++)
{
if(arr[i][j]=='m')
{
px[xl].x = i;
px[xl].y = j;
xl++;
}
if(arr[i][j]=='H')
{
py[yl].x = i;
py[yl].y = j;
yl++;
}
}
}
//cout<<xl<<endl<<yl<<endl;
}
int main()
{
while(~scanf("%d%d",&n,&m)&&n&&m)
{
input();
for(int i = 1;i<xl;i++)
{
for(int j = 1;j<yl;j++)
{
int dis = abs(px[i].x-py[j].x)+abs(px[i].y-py[j].y);
w[i][j] = -dis;
//cout<<-dis<<endl;
}
}
n = xl-1;
printf("%d\n",-km());
}
return 0;
}
第二种思路:
在深入理解了KM算法的修改顶标数组的操作后,可以通过变更修改操作,使得求出的是最小权值。。
原版的KM算法,两个点的顶标值和是大于等于这两点的边的权值的,所以相等子图存在的一定是权值大的那部分边,,
那么如果两点顶标的值的和小于等于这两点的边的权值,那么相等子图存在的一定是权值小的那部分边,
所以slack数组的值仍然是顶标和与边权值的绝对值,,每次更新同样取最小的slack值d。
更新顶标时,注意lx[i]这时要+=d(新加入的边权值是增大的),ly[i]-=d;
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 103;
const int inf = 0x3f3f3f3f;
int w[maxn][maxn],lx[maxn],ly[maxn],slack[maxn],link[maxn],visx[maxn],visy[maxn],arr[maxn][maxn];
int n,m;
bool dfs(int u)
{
visx[u] = 1;
for(int v = 1;v<=n;v++)
{
if(!visy[v])
{
int gap = -lx[u]-ly[v]+w[u][v];//原版的是lx[u]+ly[v]-w[u][v]
if(gap==0)
{
visy[v] = 1;
if(link[v]==-1||dfs(link[v]))
{
link[v] = u;
return true;
}
}
else
{
slack[v] = min(slack[v],gap);
}
}
}
return false;
}
void update()
{
int d = inf;
for(int i = 1;i<=n;i++)
{
if(!visy[i])
{
d = min(d,slack[i]);
}
}
for(int i = 1;i<=n;i++)
{
if(visx[i])
lx[i] += d;
if(visy[i])
ly[i] -= d;
}//原版为lx[i]-=d,ly[i]+=d;
}
int km()
{
memset(link,-1,sizeof(link));
for(int i = 1;i<=n;i++)
{
ly[i] = 0,lx[i] = inf;
for(int j = 1;j<=n;j++)
{
//cout<<w[i][j]<<endl;
lx[i] = min(lx[i],w[i][j]);
}
//cout<<lx[i]<<endl;
}
for(int i = 1;i<=n;i++)
{
memset(slack,inf,sizeof(slack));
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(dfs(i))
break;
else
update();
}
}
int ans = 0;
for(int i = 1;i<=n;i++)
{
ans += lx[i]+ly[i];
}
return ans;
}
struct node
{
int x,y;
}px[maxn],py[maxn];
int xl,yl;
void input()
{
xl = yl = 1;
for(int i = 0;i<n;i++)
{
getchar();
for(int j = 0;j<m;j++)
{
scanf("%c",&arr[i][j]);
}
}
for(int i = 0;i<n;i++)
{
for(int j = 0;j<m;j++)
{
if(arr[i][j]=='m')
{
px[xl].x = i;
px[xl].y = j;
xl++;
}
if(arr[i][j]=='H')
{
py[yl].x = i;
py[yl].y = j;
yl++;
}
}
}
//cout<<xl<<endl<<yl<<endl;
}
int main()
{
while(~scanf("%d%d",&n,&m)&&n&&m)
{
input();
for(int i = 1;i<xl;i++)
{
for(int j = 1;j<yl;j++)
{
int dis = abs(px[i].x-py[j].x)+abs(px[i].y-py[j].y);
w[i][j] = dis;
//cout<<dis<<endl;
}
}
n = xl-1;
printf("%d\n",km());
}
return 0;
}
本文介绍两种求解最小权值完美匹配问题的方法,一种通过取边权的负值并利用标准KM算法解决,另一种通过对KM算法进行适当修改实现直接求解最小权值匹配。
8万+

被折叠的 条评论
为什么被折叠?



