推荐系统
来源: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;
}