OJ:http://acm.hdu.edu.cn/showproblem.php?pid=3729
题意:n个学生报分数,但是他们只说了自己的分数在哪个区间,没有说具体的值,老师想知道最多可能有多少学生说的是真话,让我们输出最多的人数,和哪些学生说了真话,输出的学生编号要是从小到大的,但是又是字典序最大的那一个。
这个题是我刚接触到匈牙利算法的一个模板题,我这里主要不是讲解匈牙利算法,匈牙利算法推荐一个我觉得讲的非常不错的文章:https://blog.csdn.net/dark_scope/article/details/8880547/
匈牙利的用处在于求二分图的最大匹配。什么叫二分图,即给定的一个G(v,e),图中的点集和v能够完全的被划分到两个图中,形成两个新的点集合,且划分成两个图之后,这两个图中各自图中的任意的两个点都不会被一个边连接,也就是说在每个图中这些点都是独立的点了。
好了回到主题吧,其实题目中也就是给了我们两个点集合,一个是学生,一个是分数,每一个学生可以匹配到一个或者多个分数,根据其所说的分数区间确定。这就是一个非常明显的模板题了,就是用学生去匹配分数,看这些分数最多能匹配多少个学生。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 100005
struct stu{
int x,y;
}st[65];
int used[MAX];
int score[MAX];
int ans[65];
int find(int cur){
for(int i=st[cur].x;i<=st[cur].y;i++){
if(!used[i]){
// 暂时预订当前分数
used[i] = 1;
// 当前分数没有被使用,或者向其他人借用分数成功
// 如果其他人让出当前分数之后其他人就不能得到分数,那么他就不会让
if(score[i] == -1 || find(score[i])){
score[i] = cur;
return 1;
}
used[i] = 0;
}
}
return 0;
}
int main()
{
int t,n;
scanf("%d",&t);
for(int i=0;i<t;i++){
scanf("%d",&n);
for(int j=0;j<n;j++){
scanf("%d%d",&st[j].x,&st[j].y);
}
memset(score,-1,sizeof(score));
memset(ans,0,sizeof(ans));
int res = 0;
// 为了获得最大的字典序,所以让学号大的尽量先选择
for(int j=n-1;j>=0;j--){
memset(used,0,sizeof(used));
ans[j] = find(j);
if(ans[j]){
res++;
}
}
printf("%d\n",res);
int c = 0;
for(int j=0;j<n;j++){
if(ans[j]){
c++;
// 为了满足输出结果做的处理
if(c == res){
printf("%d",j+1);
}else{
printf("%d ",j+1);
}
}
}
printf("\n");
}
return 0;
}
本文介绍了一个利用匈牙利算法解决学生分数区间匹配问题的实例,旨在找到最多可能说真话的学生数量及具体编号。通过将学生与分数视为二分图的两个点集,采用匈牙利算法求最大匹配,实现分数区间内的最优匹配。

160

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



