问题描述
今年高考后,老师在班上做了一个关于学生成绩的调查。这个班有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);
}
}
}
}