CCF計算機軟件能力認證試題練習:201909-4 推薦系統

推薦系統

來源: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章