推薦系統
來源: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;
}