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

posted on 2010-06-22 12:53  ylfdrib  阅读(543)  评论(2编辑  收藏  举报