题目:http://codevs.cn/problem/1027/
思路:先使用匈牙利算法求解一个最长匹配解。在修改第一次求解的匹配关系,检测是否还存在其他解,如果有则无法确定对应关系,如果没有则表示匹配唯一。输入过程中如果发现一一对应关系,则通过递归来修整名字与ID的对应表。
题解:
/* 1027 姓名与ID */
#include <stdio.h>
#include <string.h>
#define MAXN 21
#define MAXID 25
int name_id[MAXN][MAXN]; /* 名字-ID对应表 */
int find_id[MAXN]; /* 已匹配ID表 */
int n; /* 所有人数 */
char id[MAXN][MAXID]; /* 所有ID */
char name[MAXN][MAXID]; /* 所有名字 */
int ni[MAXN]; /* 名字索引 */
int count; /* 名字计数 */
int in_room[MAXN]; /* 在房间人数表 */
int done[MAXN]; /* ID已存在唯一值标记 */
int state[MAXN]; /* 搜索状态标记 */
int result[MAXN]; /* 第一次匹配结果存储 */
int result1[MAXN]; /* 可能存在其他匹配结果存储 */
int ans; /* 匹配长度值 */
/* 匈牙利算法求解最长匹配 */
int find(int a, int flag[MAXN])
{
int i;
for(i = 1; i <= n; i++)
{
if(1 == name_id[a][i] && 0 == state[i])
{
state[i] = 1;
if(0 == flag[i] || find(flag[i], flag))
{
flag[i] = a;
return 1;
}
}
}
return 0;
}
/* 检测编号为i的ID与名字是否存在匹配 */
int check(int i)
{
int p, tp; /* 名字索引与匹配名字记录 */
int c = 0; /* 存在可能名字计数 */
for(p = 1; p <= n; p++)
{
if(1 == name_id[p][i])
{
c = c + 1;
tp = p;
}
}
/* 存在唯一匹配,返回匹配名字编号,否则返回0 */
if(1 == c)
{
return tp;
}
return 0;
}
/* 删除已匹配名字,整理名字-ID对应表 */
void prune()
{
int i, p;
for(i = 1; i <= n; i++)
{
/* 从未匹配ID表中,删除已匹配的名字 */
if(0 == done[i])
{
for(p = 1; p <= n; p++)
{
if(0 < find_id[p] && 1 == name_id[p][i])
{
name_id[p][i] = 0;
}
}
/* 检测编号为i的ID是否已匹配成功 */
if(0 < (done[i] = check(i)))
{
find_id[done[i]] = i;
prune();
}
}
}
}
/* 更新ID-名字链接表 */
void update(int i)
{
int p;
/* 从名字-ID对应表中,删除不在房间的名字 */
for(p = 1; p <= n; p++)
{
/* 人不在房间且与ID存在对应关系 */
if(0 == in_room[p] && 1 == name_id[p][i])
{
name_id[p][i] = 0;
}
}
/* 检测编号为i的ID是否已匹配成功 */
if(0 < (done[i] = check(i)))
{
find_id[done[i]] = i;
prune();
}
}
/* 获取ID的索引值 */
int id_num(char str[MAXN])
{
int i;
for(i = 1; i <= n; i++)
{
if(0 == strcmp(id[i], str))
{
return i;
}
}
return 0;
}
/* 添加新名字,已存在则返回名字编号 */
int name_num(char str[MAXID])
{
int i;
for(i = 0; i < count; i++)
{
if(0 == strcmp(str, name[i + 1]))
{
break;
}
}
/* 到达搜索边界,添加新名字 */
if(i == count)
{
strcpy(name[i + 1], str);
count++;
}
return i + 1;
}
/* 排序名字 */
void sort()
{
int i, j , t;
for(i = 1; i <= n; i++)
{
for(j = i + 1; j <= n; j++)
{
if(strcmp(name[ni[i]], name[ni[j]]) > 0)
{
t = ni[i];
ni[i] = ni[j];
ni[j] = t;
}
}
}
}
/* 主函数入口 */
int main()
{
int i, j , p; /* 索引值 */
int tmp_id; /* 临时ID编号,用于第二次算法搜索 */
char act, str[MAXID]; /* 动作与输入字符串暂存 */
/* 打开数据文件 */
FILE *fp;
if(NULL == (fp = fopen("data.txt", "r")))
{
return 1;
}
/***************/
/* 获取所有ID数 */
fscanf(fp, "%d", &n);
/* 获取所有ID并初始化ID链表 */
for(i = 1; i <= n; i++)
{
fscanf(fp, "%s\n", id[i]);
/* 初始化ID与名字对应表 */
for(p = 1; p <= n; p++)
{
name_id[p][i] = 1;
}
/* 初始化房间 */
in_room[i] = 0;
}
/* 获取人员动作 */
count = 0;
fscanf(fp, "%c", &act);
while('Q' != act)
{
/* 读取名字 */
fscanf(fp, "%s\n", str);
/* 处理动作 */
if('E' == act)
{
ni[name_num(str)] = name_num(str);
in_room[ni[name_num(str)]] = 1;
}
else if ('L' == act)
{
in_room[ni[name_num(str)]] = 0;
}
else if('M' == act)
{
update(id_num(str));
}
/* 读取下一条数据 */
fscanf(fp, "%c", &act);
}
/* 测试 - 打印所有名字 */
/*
for(i = 1; i <= n; i++)
{
printf("%d: %s \n", i, name[ni[i]]);
}
sort1();
printf("Sort: \n");
for(i = 1; i <= n; i++)
{
printf("%d: %s \n", i, name[ni[i]]);
}
*/
/**********************/
/* 测试-打印最终ID对应表 */
/*
for(p = 1; p <= n; p++)
{
printf("%s : ", name[p]);
for(i = 1; i <= n; i++)
{
if(1 == name_id[p][i])
{
printf("%s\t", id[i]);
}
}
printf("\n-----------------\n");
}
*/
/**************************************/
/* 做一次匈牙利算法,求一次可能匹配 */
for(i = 1; i <= n; i++)
{
result[i] = 0;
}
ans = 0;
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
state[j] = 0;
}
if(1 == find(i, result))
{
ans++;
}
}
/* 测试 - 打印匹配关系 */
/*
printf("Ans = %d\n", ans);
for(p = 1; p <= n; p++)
{
printf("%-8s -> %-8s\n", name[result[p]], id[p]);
}
printf("---------------\n");
*/
/*************************************/
for(p = 1; p <= n; p++)
{
if(find_id[p] > 0)
{
continue;
}
/* 初始化匹配关系 */
for(i = 1; i <= n; i++)
{
result1[i] = 0;
}
/* 找寻第一次匹配时,与名字i对应的ID编号tmp_id */
for(i = 1; i <= n; i++)
{
if(result[i] == p)
{
tmp_id = i;
break;
}
}
/* 修改名字与ID之间的对应关系 */
name_id[p][tmp_id] = 0;
ans = 0;
/* 重新进行一次算法求解匹配 */
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
state[j] = 0;
}
if(1 == find(i, result1))
{
ans++;
}
}
/* 如果不能完全匹配,则表示是唯一解 */
if(ans < n)
{
find_id[p] = tmp_id;
}
/* 测试 - 打印匹配关系 */
/*
printf("Ans = %d\n", ans);
for(p = 1; p <= n; p++)
{
printf("%-8s -> %-8s\n", name[result1[p]], id[p]);
}
printf("---------------\n");
*/
/*************************************/
/* 恢复对应关系 */
name_id[p][tmp_id] = 1;
}
/* 排序名字 */
sort();
/* 打印结果值 */
for(i = 1; i <= n; i++)
{
if(0 != find_id[ni[i]])
{
printf("%s:%s\n", name[ni[i]], id[find_id[ni[i]]]);
}
else
{
printf("%s:???\n", name[ni[i]]);
}
}
/*****************/
/* 关闭文件 */
fclose(fp);
/************/
return 0;
}