PS:自己的第一道二分图匹配,觉得这题作为二分匹配的入门指导题不错,
写下题解,增加自己对二分图的最大匹配的理解吧!
题意: 题目是老师想知道他的学生在这次的考试考得咋样,于是他就向他的学生提问,然而他的学生都不想告诉他他们的成绩,只肯说他们在级里的排名,并且有些学生会说谎,谎报排名等级,于是老师想知道究竟有多少个人没有说谎,因为老师很相信他的学生,所以他想知道最多的人说实话的人数;
意解: 题目是想找出最大的匹配数,所以可以用二分图的最大匹配的思想来解题,具体可以用匈牙利算法解。每次可以在一个区间中找出可以匹配的等级,找不到就递归继续查找;最大匹配: 首先可以把学生的编号作为一个点集,把学生的排名等级作为另一个等级,然后就可以构造最大匹配了。
PS:一个二分图中的最大匹配数等于这个图中的最小点覆盖数
König定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数。如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖所有的边。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ls u << 1
#define rs u << 1 | 1
#define lson l, mid, u << 1
#define rson mid + 1, r, u << 1 | 1
using namespace std;
const int M = 1e5 + 5;
int x[66],y[66],vis[M],line[M];
/***************************/
/* 二分图的最大匹配
/* 人的编号和排名构成二分图
/* 题目因为要按最大的字典序输出
/* 所以可以从后面往前匹配
/* 递归搜索可以匹配的边
/***************************/
bool find(int n)
{
for(int i = x[n]; i <= y[n]; i++)
{
if(!vis[i])
{
vis[i] = 1;
if(!line[i] || find(line[i])) //如果当前的等级已经被匹配,则递归搜索还未匹配的等级
{
line[i] = n; //把当前的人和等级连一条边;
return true;
}
}
}
return false;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int t,n;
scanf("%d",&t);
while(t--)
{
fill(line,line + M, 0);
int ans[66],res = 0;
scanf("%d",&n);
for(int i = 0; i < n; i++)
{
scanf("%d %d",x + i, y + i);
}
for(int i = n - 1; i >= 0; i--)
{
fill(vis,vis + M, 0); //记得初始化辅助数组,记录当前的等级是否已经被匹配;
if(find(i)) ans[res++] = i + 1; //找到匹配的入数组;
}
printf("%d\n", res);
for(int i = res - 1; i >= 0; i--)
{
if(i == 0) printf("%d\n",ans[i]);
else printf("%d ",ans[i]);
}
}
return 0;
}