看到其它题解代码颇长,蒟蒻在此贡献一个二分图最大匹配的解法。
题意
鞋店里有 \(n\) ( \(1 \le n \le 10^5\) ) 双鞋子,第 \(i\) 双鞋子有价格 \(c_i\) 与尺码 \(s_i\) ( \(1 \le c_i, s_i \le 10^9\) ),保证 \(s_i\) 互不相同。
有 \(m\) ( \(1 \le m \le 10^5\) ) 位顾客光临,第 \(i\) 位顾客有 \(d_i\) ( \(1 \le d_i \le 10^9\) ) 元钱,他可以穿尺码为 \(l_i\) 或者 \(l_i + 1\) 的鞋子。
求出一种将鞋子卖给顾客的方法,使得获利最大,输出方案。
题解
观察到题目相当于将顾客与鞋子进行匹配,因为 \(s_i\) 互不相同,所以每个顾客最多可以匹配两个鞋子。问题转化为:有一个二分图,左右分别有 \(n, m\) 个点,左边的点有点权,右边的每个点的度数不超过 \(2\),求最大带权匹配。
注意到右边每个点度数不大于 \(2\),这保证了可以用匈牙利算法跑二分图最大带权匹配,若 \(n, m\) 同阶那么复杂度是 \(O(n)\) 的。
我们贪心地将左边的点按点权从大到小排序,然后依次匹配,要求每次匹配不能挤掉已经匹配的点,这样答案一定是最大的。
代码非常好写:
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, r, l) for (int i = r; i >= l; --i)
using namespace std;
using i64 = long long;
const int N = 1E5 + 5;
int n, m, L[N], vis[N], ans;
i64 Ans;
map<int, int> mp;
vector<int> adj[N];
struct shoe {int c, s, id;} a[N];
bool cmp(shoe a, shoe b) {return a.c > b.c;}
bool match(int x, int f) {
if (vis[x] == f) return 0; vis[x] = f;
for (auto v : adj[x]) {
if (!L[v] || match(L[v], f)) return L[v] = x, 1;
} return 0;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(nullptr);
cin >> n;
rep(i, 1, n) cin >> a[i].c >> a[i].s, a[i].id = i;
sort(a + 1, a + 1 + n, cmp);
rep(i, 1, n) mp[a[i].s] = i;
cin >> m;
rep(i, 1, m) {
int d, l; cin >> d >> l;
auto check = [&](int k) {
if (mp.count(k) && a[mp[k]].c <= d)
adj[mp[k]].push_back(i);
} ;
check(l); check(l + 1);
}
rep(i, 1, n) if (match(i, i)) ++ans, Ans += a[i].c;
cout << Ans << '\n' << ans << '\n';
rep(i, 1, m) if (L[i]) cout << i << ' ' << a[L[i]].id << '\n';
}
标签:le,匹配,int,题解,cin,CF166D,鞋子
From: https://www.cnblogs.com/CTHOOH/p/18259093