题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2378
题目大意:给你一个 n*n 的矩阵,要求你对每行和每列设一个值 row(i)和col(i),设的对于任意格子w( i , j ) ,满足 w( i , j ) <= row( i ) + col( j ) ,并且所有的 row( i ) 和 col( i )的和最小。
解题思路:这个在二分图匹配中粗现,但是实在看不出有什么可以和二分图联系上的,后来看了书才发现,原来这道题是 KM 算法的一个副产品。在 KM 里顶标 Lx 和 Ly,Lx( x ) + Ly( y ) >= w( x , y ) ,然后就根据这个不等式,直接上 KM 就行。算法结束后,所有的顶标和是最小的。 (这句话需要好好理解!)
很好的一道开拓性思维的题目啊!~~~~
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff;
const int MAXN = 555;
int n;
int w[MAXN][MAXN];
int lx[MAXN],ly[MAXN],slack[MAXN];
int s[MAXN],t[MAXN],left[MAXN];
int match(int i)
{
s[i] = 1;
for(int j = 1;j <= n;j++)
if(!t[j])
{
int tmp = lx[i]+ly[j]-w[i][j];
if(tmp == 0)
{
t[j] = 1;
if(!left[j] || match(left[j]))
{
left[j] = i;
return 1;
}
}
else slack[j] = min(slack[j],tmp);
}
return 0;
}
void update()
{
int a = INF;
for(int i = 1;i <= n;i++)
if(!t[i]) a = min(a,slack[i]);
for(int i = 1;i <= n;i++)
{
if(s[i]) lx[i] -= a;
if(t[i]) ly[i] += a;
}
}
void km()
{
for(int i = 1;i <= n;i++)
{
left[i] = lx[i] = ly[i] = 0;
for(int j = 1;j <= n;j++)
lx[i] = max(lx[i],w[i][j]);
}
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
slack[j] = INF;
while(1)
{
for(int j = 1;j <= n;j++) s[j] = t[j] = 0;
if(match(i))
break;
else update();
}
}
}
int main()
{
while(~scanf("%d",&n))
{
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
scanf("%d",&w[i][j]);
km();
int sum = lx[1];
printf("%d",lx[1]);
for(int i = 2;i <= n;i++)
{
sum += lx[i];
printf(" %d",lx[i]);
}
puts("");
sum += ly[1];
printf("%d",ly[1]);
for(int i = 2;i <= n;i++)
{
sum += ly[i];
printf(" %d",ly[i]);
}
puts("");
printf("%d\n",sum);
}
return 0;
}