题意:
一共n个人,m个物品,每个人都要选择一个物品,一个物品不能匹配多个人,每个物品有两种属性w和c,每个人都有两个要求a和b,一个物品可以匹配一个人,当且仅当w>=a和c>=b。求出一种符合的匹配策略,如果不可能就输出-1。
思路:
一道经典的贪心题,二维属性的匹配问题。
先来考虑一维的情况,当每个物品只有一个属性的时候,很显然,贪心策略是为每一个物品找到所有可以匹配这个物品的人中要求值最大的人来匹配。
当情况变成二维x,y的时候,贪心策略就是对于每个物品找到满足w<x的所有人中c<y且c最大的人来匹配。
但是对于每个物品找到x匹配的人,如果O(n*m)复杂度很高不能满足,这里有一种可以优化的方法,将人和物品的序列都按照从小到大排序,这样设置两个变量i,j一起变化,每对应一个i,j都会从上次的位置向后移动一些,虽然二重循环,但实际上两个序列都只遍历了一遍,这样复杂度就是O(n+m)。至于寻找最大的y值,则可以利用stl将复杂度固定在O(logm),整个算法的复杂度就是O((n+m)*logm),可以满足题目要求。另外这道题还需要输出结果,所以就需要保存id。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> Pair;
const int MAXN = 2e5 + 10;
struct node {
int x, y, id;
bool operator < (const node &c) const {
return x < c.x;
}
}a[MAXN], b[MAXN];
multiset <int> st;
multiset <int> :: iterator it;
map <int, queue <int> > mp;
int ans[MAXN];
int main() {
//freopen("in", "r", stdin);
int n, m;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i].x, &a[i].y);
a[i].id = i;
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%d%d", &b[i].x, &b[i].y);
b[i].id = i;
}
sort (a + 1, a + 1 + n);
sort (b + 1, b + 1 + m);
int cnt = 0;
for (int i = 1, j = 1; i <= m; i++) {
for (; j <= n && b[i].x >= a[j].x; j++) {
st.insert(a[j].y);
mp[a[j].y].push(a[j].id);
}
if (st.empty()) continue;
it = st.upper_bound(b[i].y);
if (it == st.begin()) continue;
if (*--it <= b[i].y) {
int id = mp[*it].front(); mp[*it].pop();
ans[id] = b[i].id;
st.erase(it);
++cnt;
}
}
if (cnt != n) {
puts("-1");
return 0;
}
//printf("%d\n", cnt);
for (int i = 1; i <= n; i++)
printf("%d%c", ans[i], i == n ? '\n' : ' ');
return 0;
}