I'm Telling the Truth(二分图匹配)

原题

1
2
3
4

题意:

有n个学生,你想了解他们的成绩。每个人告诉你他们的名次区间在x~y之间(闭区间),你需要判断最多说真话的人数,并输出这些人的编号,多种情况时输出最大字典序的一组

思路

该题目乍一看像并查集的题目,判段前面的情况真假,然后后面的在前面的基础上…实则不然
仔细一想,就是求人数和名次最多能配对多少组,就是一道二分图匹配的问题。
对于输出最大字典序的问题,只要在记录最大匹配对的时候,从n~1进行遍历即可,然后查找函数found中,遍历每个人的名次区间即可。(之前遇见相当于都是一个整的大区间,这也算是不同情况)

代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define mem(x,y) memset(x,y,sizeof(x))
#define N 100010
int used[N],line[N],b[N];//每次found各节点的使用情况;当前名次对应的人的编号;标记
struct node
{
    int x,y;
}a[100];
bool found(int n)
{
    for(int i=a[n].x;i<=a[n].y;i++)  //i的范围即n可取的范围,可能是一个区间,或给定的联系
    {
        if(!used[i]){
            used[i]=1;
            if(line[i]==0||found(line[i])){
                line[i]=n;
                b[n]=i;   //标记一下n点已经被征用
                /*注意在此,若后来的节点影响到已匹配好的节点
                 那么如果该节点成功匹配
                则其影响到的节点全部都成功匹配,
                所以被影响到的b[n]不需要清零标记
                */
               return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int t,n,x,y;
    scanf("%d",&t);
    while(t--){
        mem(b,0),mem(line,0);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&a[i].x,&a[i].y);
        int ans=0;
        for(int i=n;i>=1;i--)  //从n——1即可得到最大字典序的情况(很实用的技巧)
        {
            mem(used,0);   //必备
             if(found(i)) ans++;
        }
        printf("%d\n",ans);
        for(int i=1;i<=n;i++){
            if(b[i])
            {
                ans--;
                if(ans) printf("%d ",i);
                else printf("%d\n",i);
            }
        }
    }
    return 0;
}

发布了85 篇原创文章 · 获赞 10 · 访问量 5229

猜你喜欢

转载自blog.csdn.net/riversuer/article/details/105420920
今日推荐