HDU-3729-I'm Telling the Truth(最大匹配)

探讨了一道关于高考成绩调查的问题,通过构建学生与成绩区间的关系图,并使用最大匹配算法来找出说真话的学生数量及具体学生编号,提供了一种解决此类问题的有效算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

今年高考后,老师在班上做了一个关于学生成绩的调查。这个班有n个学生。学生们不想告诉老师他们的确切分数;他们只告诉老师他们在省里的级别(以间隔的形式)。

老师问了所有的学生之后,发现有些学生没有说实话。例如,学生1说他在5004到5005之间,学生2说他在5005到5006之间,学生3说他在5004到5006之间,学生4说他也在5004到5006之间。这种情况显然是不可能的。所以至少有一个人说了谎。因为老师认为他的学生大多数是诚实的,他想知道有多少学生说的最多是真话。

输入

第一行中有一个整数,表示案例的数量(最多100个案例)。在每种情况的第一行中,整数n (n <= 60)表示学生的数量。在接下来的n行每一种情况中,每一行都有2个数字,习和Yi(1 <=习<= Yi <= 100000),表示第i个学生的秩在习和Yi之间,包括。

输出

为每种情况输出2行。在第一行输出一个数字,这意味着说真话的学生人数最多。在第二行,输出讲真话的学生,用空格隔开。请注意,每一行的头尾都没有空格。如果有不止一种方法,则输出具有最大字典顺序的列表。(在上面的例子中,1 2 3;1 2 4;1 3 4;)

题解:将每个学生的id向其成绩区间的每一点都连上去,求一个最大匹配。

#include <cstdio>
#include <cstring>
#define m(a,b) memset(a,b,sizeof a)
using namespace std;
const int N=75;
const int M=100000+5;
int tot;
int head[N],mx[N],my[M];
int vis[M];
struct Edge{int to,nex;}edge[N*M];
template<class T>void rd(T &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9')  {f|=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return;
}
void add(int from,int to)
{
    edge[++tot]=(Edge){to,head[from]};head[from]=tot;
}
int find_path(int x)
{
    for(int i=head[x];i;i=edge[i].nex)
    {
        int y=edge[i].to;
        if(!vis[y])
        {
            vis[y]=1;
            if(!my[y]||find_path(my[y]))
            {
                mx[x]=y,my[y]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int T;rd(T);
    while(T--)
    {
        m(head,0),m(my,0),m(mx,0);
        tot=1;
        int n;rd(n);
        for(int i=1;i<=n;i++)
        {
            int x,y;
            rd(x),rd(y);
            for(int j=x;j<=y;j++)
                add(i,j);
        }
        int ans=0;
        for(int i=n;i>=1;i--)
        {
            m(vis,0);
            if(find_path(i))
                ans++;
        }
        printf("%d\n",ans);
        for(int i=1;i<=n;i++)
        {
            if(mx[i])
            {
                --ans;      //为了判断是不是输出答案的最后一个数从而控制格式。
                if(ans)
                    printf("%d ",i);
                else
                    printf("%d\n",i);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值