【ccf】201909-4推荐系统(C++)【多路归并+set维护有序序列】

更多题目请看CCF历年真题汇总

题目描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
输入

2 3
1 3
2 2
3 1
8
3 100 1 1
1 0 4 3
1 0 5 1
3 10 2 2
3 10 1 1
2 0 1
3 2 1 1
3 1 1 1

输出

1
1
1 4
1 2
1
1
4
1
4
-1

思路

  • set 可以在nlogn 复杂度之内动态维护一个有序序列。所以用set<PII>维护每一类商品的{id, score}. (PII 是 pair<int,int>)
    • score是从大到小小选,所以需要一个反向迭代器 set<PII>reverse_iterator
    • 选择商品的时候,只需要在每一类开头选择分数最大的即可.
  • 更多细节在代码中

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 55;
typedef pair<int,int> PII;

int n, m;

set<PII> g[N]; //第i类的score 和 id
set<PII>::reverse_iterator it[N];

map<int,int> f[N]; //删除的时候只有编号,所以要存储编号对应的分数,便于在集合里面删除元素

vector<int> ans[N]; //每个查询的答案。第i类选择了哪些

int main()
{
    cin >> m >> n;
    for(int i=0; i<n; i++) 
    {
        int id, sc;
        cin >> id >> sc;
        for(int j=0; j<m; j++) {
            g[j].insert({sc, -id}); //主要以分数排序,-id存储可以保证本身小的id在后面。可以被反向迭代器先遍历到。
            f[j][id] = sc;
        }
    }
    int q; cin >> q;
    while(q --)
    {
        int op; cin >> op;
        if(op == 1) //增加
        {
            int type, id, score;
            // cin >> type >> id >> score;
            scanf("%d%d%d", &type, &id, &score);
            f[type][id] = score;
            g[type].insert({score, -id});
        }
        else if(op == 2) //删除
        {
            int type, id;
            // cin >> type >> id;
            scanf("%d%d", &type, &id);
            g[type].erase({f[type][id], -id});
            f[type].erase(id);
        }
        else //查询
        {
            int tot;
            cin >> tot;
            int sum[N], cnt[N]; //第i类最多sum个,已经选了cnt个
            /*
            * 这个地方两个数组如果定义在这里,可能不会超时
            * 如果定义的是全局变量,就大概率会超时。。。。
            * 
            * 输入改成scanf不会超时!! 所有输入改成scanf好了。。。
            *
            */
            for(int i=0; i<m; i++) { //输入每个类别的数量限制
                // cin >> sum[i];
                scanf("%d", &sum[i]);
                cnt[i] = 0; //清空数组
                it[i] = g[i].rbegin(); //初始化反向迭代器
                ans[i].clear();//初始化结果数组
            }
            while(tot--) //选不超过tot个
            {
                int k = -1;
                for(int i=0; i<m; i++)  //找到m类中当前最大的一个
                {
                    if(it[i] != g[i].rend() && cnt[i] < sum[i]) //第i类还有商品,且数量没有达到限制
                    {
                        if(k == -1 || it[k]->first < it[i]->first) { //还没选或者比第k分数类更大
                            k = i;
                        }
                    }
                        
                }
                if(k == -1) break; //什么都选不出来了
                ans[k].push_back(-it[k]->second); //编号存的时候取反了,要变回来
                it[k] ++; //第k类下一个
                cnt[k] ++; //数量++
            }
            for(int i=0; i<m; i++) { //输出这次查询的答案
                if(ans[i].empty()) puts("-1");
                else {
                    for(auto x:ans[i]) {
                        cout << x << ' ';
                    }
                    cout << endl;
                }
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值