hdu2426 Interesting Housing Problem

http://acm.hdu.edu.cn/showproblem.php?pid=2426

题目大意:住房分配。现有N个学生,M间房。每个学生对一部分房有评估值,用一个整数表示,正数表示喜欢,0表示中立,负数表示不喜欢,没有评估的不能住。校长要给这些学生分房,要求每个学生都不住他们不喜欢住的房。如果可能,输出最大的满意度,否则,输出-1。

算法分析:最优二分匹配(或称最佳匹配)。用KM算法解决。

这是我第一次写KM,写的还是很烂。百度百科有KM算法介绍(http://baike.baidu.com/view/739278.htm?fr=ala0_1_1),写的还可以。用百度模板写了一下。跑了1671ms。

代码
   
     
#include < stdio.h >
#include
< string .h >
#define NN 502
#define INF 0xfffffff

int visitx[NN], visity[NN], mat[NN][NN];
int x[NN], y[NN], link[NN];
int N, M, slack;

int find( int t)
{
int i, tmp;
visitx[t]
= 1 ;
for (i = 0 ; i < M; i ++ ){
if ( ! visity[i]){
tmp
= x[t] + y[i] - mat[t][i];
if (tmp == 0 )
{
visity[i]
= 1 ;
if (link[i] == - 1 || find(link[i])){
link[i]
= t;
return 1 ;
}
}
else if (tmp < slack)
slack
= tmp;
}
}

return 0 ;
}
void KM()
{
int i, j;

for (i = 0 ; i < N; i ++ ){
x[i]
= 0 ;
for (j = 0 ; j < M; j ++ ){
if (mat[i][j] > x[i])
x[i]
= mat[i][j];
}
}

for (j = 0 ; j < M; j ++ ){
y[j]
= 0 ;
}

memset(link,
- 1 , sizeof (link));
for (i = 0 ; i < N; i ++ ){
while ( 1 )
{
memset(visitx,
0 , sizeof (visitx));
memset(visity,
0 , sizeof (visity));

slack
= INF;
if (find(i)) break ;

for (j = 0 ; j < N; j ++ ){
if (visitx[j]) x[j] -= slack;
}

for (j = 0 ; j < M; j ++ ){
if (visity[j]) y[j] += slack;
}
}
}
}
int main()
{
int i, j, a, b, c, E, ans, t, cnt;
int icase = 1 ;
while (scanf( " %d%d%d " , & N, & M, & E) != EOF){
for (i = 0 ; i < N; i ++ )
for (j = 0 ; j < M; j ++ )
mat[i][j]
= - INF;
if (E == 0 ){
printf(
" Case %d: " , icase ++ );
puts(
" -1 " );
continue ;
}
while (E -- ){
scanf(
" %d%d%d " , & a, & b, & c);
if (c < 0 )
continue ;
mat[a][b]
= c;
}

KM();

printf(
" Case %d: " , icase ++ );

ans
= 0 ;
cnt
= 0 ;
for (i = 0 ; i < M; i ++ ){
t
= link[i];
if (t >= 0 && mat[t][i] != - INF){
cnt
++ ;
ans
+= mat[t][i];
}
}

if (cnt < N)
ans
= - 1 ;
printf(
" %d\n " , ans);
}
return 0 ;
}

 这个模板和文字说的好像不太一样,不知是不是O(n3)的。自己又收了了个,和文字介绍的一个意思,每一个y节点加了一个松弛量,用数组slack[]保存,比较快390ms,但我个人并没有感觉到这两个写法的区别,待考察。

代码
   
     
#include < stdio.h >
#include
< string .h >
#define NN 502
#define INF 0xfffffff

int visitx[NN], visity[NN], mat[NN][NN];
int x[NN], y[NN], link[NN];
int N, M, slack[NN];

int find( int t)
{
int i, tmp;
visitx[t]
= 1 ;
for (i = 0 ; i < M; i ++ ){
if ( ! visity[i]){
tmp
= x[t] + y[i] - mat[t][i];
if (tmp == 0 )
{
visity[i]
= 1 ;
if (link[i] == - 1 || find(link[i])){
link[i]
= t;
return 1 ;
}
}
else if (tmp < slack[i])
slack[i]
= tmp;
}
}

return 0 ;
}
void KM()
{
int i, j, min;

for (i = 0 ; i < N; i ++ ){
x[i]
= 0 ;
for (j = 0 ; j < M; j ++ ){
if (mat[i][j] > x[i])
x[i]
= mat[i][j];
}
}

for (j = 0 ; j < M; j ++ ){
y[j]
= 0 ;
}

memset(link,
- 1 , sizeof (link));
for (i = 0 ; i < N; i ++ ){

for (j = 0 ; j < M; j ++ )
slack[j]
= INF;
while ( 1 )
{
memset(visitx,
0 , sizeof (visitx));
memset(visity,
0 , sizeof (visity));

min
= INF;
if (find(i)) break ;

for (j = 0 ; j < M; j ++ ){
if ( ! visity[j] && slack[j] < min)
min
= slack[j];
}
for (j = 0 ; j < N; j ++ ){
if (visitx[j]) x[j] -= min;
}

for (j = 0 ; j < M; j ++ ){
if (visity[j]) y[j] += min;
else slack[j] -= min;
}
}
}
}
int main()
{
int i, j, a, b, c, E, ans, t, cnt;
int icase = 1 ;
while (scanf( " %d%d%d " , & N, & M, & E) != EOF){
for (i = 0 ; i < N; i ++ )
for (j = 0 ; j < M; j ++ )
mat[i][j]
= - INF;
if (E == 0 ){
printf(
" Case %d: " , icase ++ );
puts(
" -1 " );
continue ;
}
while (E -- ){
scanf(
" %d%d%d " , & a, & b, & c);
if (c < 0 )
continue ;
mat[a][b]
= c;
}

KM();

printf(
" Case %d: " , icase ++ );

ans
= 0 ;
cnt
= 0 ;
for (i = 0 ; i < M; i ++ ){
t
= link[i];
if (t >= 0 && mat[t][i] != - INF){
cnt
++ ;
ans
+= mat[t][i];
}
}

if (cnt < N)
ans
= - 1 ;
printf(
" %d\n " , ans);
}
return 0 ;
}

这题还有个小陷阱,如果估计值小于0就不用考虑了。 

注意这组测试数据

2 2 4
0 0 1
0 1 4
1 0 -1
1 1 1

正确解为2

你可能感兴趣的:(REST)