题意:
n个学生每个学生报一个自己排名的可能区间[l,r],求最多有几个学生说的是真话,并且情况合法,要求输出匹配的结果,多解的情况下要求字典序最大。
思路:
裸的二分图最大匹配,这题需要注意的一点就是要输出字典序最大的一组匹配结果。所以在进行匹配的过程中要从nl 到1逆序进行,保证下标大的人先匹配。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 65;
const int MAXM = 100005;
struct BgMaxMatch {
int nl, nr;
int l[MAXN], r[MAXM];
bool vis[MAXM];
vector <int> G[MAXN]; //这里注意要看是按左右哪边建图,也有可能是MAXM
void init(int nl, int nr) {
this -> nl = nl;
this -> nr = nr;
for (int i = 1; i <= nl; i++) G[i].clear();
}
void AddEdge(int u, int v) {
G[u].push_back(v);
}
bool dfs(int u){ // 注意这里的u始终是l数组中的点
int cnt = G[u].size();
for (int i = 0; i < cnt; i++) {
int v = G[u][i];
if (!vis[v]) {
vis[v] = true;
if(r[v] == -1 || dfs(r[v])) {
r[v] = u; l[u] = v;
return true;
}
}
}
return false;
}
int MaxMatch() {
memset(l, -1, sizeof(l));
memset(r, -1, sizeof(r));
int ans = 0;
for (int i = nl; i >= 1; i--) { // 这里逆序dfs,保证匹配结果字典序最大
memset(vis, false, sizeof(vis));
ans += dfs(i);
}
return ans;
}
} solver;
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
solver.init(n, 100000);
for (int i = 1; i <= n; i++) {
int ll, rr;
scanf("%d%d", &ll, &rr);
for (int j = ll; j <= rr; j++)
solver.AddEdge(i, j);
}
printf("%d\n", solver.MaxMatch());
vector <int> ans;
for (int i = 1; i <= solver.nl; i++) {
if (solver.l[i] != -1) ans.push_back(i);
}
int cnt = ans.size();
for (int i = 0; i < cnt; i++)
printf("%d%c", ans[i], i == cnt - 1 ? '\n' : ' ');
}
return 0;
}