CF - gym - Samara University ACM ICPC 2016-2017 Quarterfinal Qualification Contest --- G

题意:

一共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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值