HDU-3729 I'm Telling the Truth 二分图 匈牙利算法模板题

本文介绍了一个利用匈牙利算法解决学生分数区间匹配问题的实例,旨在找到最多可能说真话的学生数量及具体编号。通过将学生与分数视为二分图的两个点集,采用匈牙利算法求最大匹配,实现分数区间内的最优匹配。

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值