CCF计算机软件能力认证试题练习:201909-4 推荐系统 c++

推荐系统
来源:CCF

标签:

参考资料:

相似题目:

题目
某电商有编号为0到m-1的m类商品,包括家电、汽车、电动车、面包、化妆品等。对于每个app新用户,每类商品初始有编号不同的n个商品,包括各个商家、品牌、供应商等。在任何时刻,同类的任意两个商品的编号各不相同,不同类的任意两个商品的编号可能相同。app会给每个商品打分。初始时,各类商品的编号和得分都相同。在用户使用app时,会产生有效信息,包括喜欢、不喜欢等。app会根据这些信息,在某类商品增加或删除商品。app每次会推荐一部分商品给用户看。一个简单的想法是,选出各类所有商品中得分最大的若干商品。
该方法虽然简单,但是如果某类商品可能得分特别高,这种简单想法就无法保证推荐商品的多样性。因此,app查询得分最大的若干商品,同时限制各类商品个数不能超过一个阅值。将上述过程抽象成3中操作:操作1、2、3,分别对应增加、删除、查询操作:
1 type commodity score表示在type类商品中增加编号为commodity的商品,该商品分数为score
2 type commodity表示在type类商品中删除编号为commodity的商品。
3 k k_0 k_1 k_{m-1}表示在各类所有商品中选出不超过K个(不一定要达到K个)得分最大的商品,同时第i(0<=i<m)类商品的个数不超过k_i。在查询时,如果第a(0<=a<m)类商品中编号为b的商品和第A(0<=A<m)类商品中编号为B的商品得分相同:
1,当a=A时,选取编号为min(b,B)的商品;
2,当a≠A时,选取第min(a,A)类商品。

输入
从标准输入读入数据。
输入的第一行包含两个正整数m和n,保证n<=3x10^4和m<=50.
接下来n行,每行两个正整数id和score。第1+j(1<=j<=n)行表示所有m类商品的第j个商品的编号和得分。
接下来一行包含一个正整数opnum,表示操作总数,保证n<=10^5。其中,查询操作一共有opask个,保证opask <= 10^2.
接下来opnum行,每行若干个正整数,格式对应1 type commodity score、2
type commodity、3 K k_0 k_1 … k_{m-1},其中,K<=10^2,k_0 k_1 … k_{m-1}<=10^5.

输出
输出到标准输出。
输出共opask×m,对应opask个查询操作。第r×m+c,0<=r<opask,1<=c<=m行表示,在第r个查询操作中,第c类商品选出的商品编号,同类商品的编号从小到大输出。如果r个查询操作中,第c类商品没有选出任何商品,则该行输出-1

输入样例
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。设计商品信息结构,用来存储商品的类型、编号和得分。设计删除信息结构,用来存储将要删除的商品的类型和编号。考虑到题目输出有序且执行大量查询操作,使用set来存储数据。同时为了提高效率,实行惰性删除。参考代码

 

#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
using namespace std;

struct Goods{ //商品信息 
    int type;
    int id;
    int score;

    Goods(int _type, int _id, int _score){
        type=_type; id=_id; score=_score;
    }
    
    bool operator < (const Goods &rhs) const{
        if(score==rhs.score){
            if(type==rhs.type) return id<rhs.id;
            return type<rhs.type;
        }
        return score>rhs.score;
    }
};

struct Del{    //已删除的商品信息 
    int type;
    int id;
    
    Del(int _type, int _id){
        type=_type; id=_id;
    }
    
    bool operator < (const Del &rhs) const{
        if(type==rhs.type) return id<rhs.id;
        return type<rhs.type;
    }
}; 

set<Goods> goods; //存放所有商品信息 
set<Del> has_del; //存放所有已删除的商品信息 

int m, n;
int id, score;
int opnum;
int type;
int K, k[52]; 

int cnt[52]; //计数,cnt[i]:第i类商品中已选数目, cnt[51]已选总数 
vector<int> chosen[52]; //输出每类选中的商品的编号 

int main(){
    scanf("%d%d", &m, &n);
    for(int i=0; i<n; ++i){
        scanf("%d%d", &id, &score);
        for(int j=0; j<m; ++j){
            goods.insert(Goods(j, id, score));
        }
    }
    scanf("%d", &opnum);
    int op; //操作类型
    for(int i=0; i<opnum; ++i){
        scanf("%d", &op);
        if(op==1){ //添加操作 
            scanf("%d%d%d", &type, &id, &score);
            goods.insert(Goods(type, id, score));
        }
        else if(op==2){ //删除操作 
            scanf("%d%d", &type, &id);
            has_del.insert(Del(type, id)); //将该商品加入删除表中,实行惰性删除 
        }
        else{ //查询操作 
            scanf("%d", &K);
            for(int j=0; j<m; ++j){
                scanf("%d", &k[j]);
            }
            
            memset(cnt, 0, sizeof(cnt));
            for(int j=0; j<m; ++j) chosen[j].clear();
            
            //搜索开始! 
            for(set<Goods>::iterator it=goods.begin(); cnt[51]<K && it!=goods.end();){
                //该类商品未选满,查看该商品是否已删除 
                if(cnt[(*it).type]<k[(*it).type]){  
                    if(has_del.find(Del((*it).type, (*it).id))!=has_del.end()){ //存在于删除表中 
                        goods.erase(it++); //删除该元素,迭代器自增 
                    }
                    //未删除 
                    else{
                        ++cnt[(*it).type];
                        ++cnt[51];
                        chosen[(*it).type].push_back((*it).id); 
                        it++;
                    } 
                }
                else it++; 
            }
            
            //输出!
            for(int j=0; j<m; ++j){
                if(cnt[j]==0) printf("-1\n");
                else{
                    for(vector<int>::iterator it=chosen[j].begin(); cnt[51]<=K && it!=chosen[j].end(); ++it){
                        printf("%d ", *it);
                    }
                    printf("\n");
                }
            } 
            
        }
    } 
    
    return 0;
}
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值