原题
题意:
有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;
}