CCF CSP 201909-4 推荐系统

点击前往试题目录:https://blog.csdn.net/best335/article/details/99550556

百度搜索页标题没有更新
在这里插入图片描述

题意:

有m∈[0,49]类商品,每类商品有n∈[0,30000]个物品.
初始时,给出第i<n个商品的ID和Score,即初始时所有类商品的第i个物品的ID和Score都相同。

有三种操作:

  • 增加第type类编号为commodity分数为score的商品
  • 删除第type类编号为commodity的商品
  • 查询分数在前K个商品且每类商品个数不超过k_i

其中总操作个数OPnum <=100000,查询个数OPask<=100

求每次查询时每类商品选出的商品编号:

  • 相同分数的选择类别更小的物品
  • 类别相同分数也相同选择ID更小的物品
  • 该类若无选择输出-1,否则按编号升序输出选择的物品,每类输出占一行,共输出OPask*m行,即输出所有查询操作所对应的每类选择的升序商品编号

考点:问题需求理解能力(倒不如说出题人的表达能力)、STL的熟练应用、时间复杂度的分析、问题建模能力。

题目未描述的部分:

  • 增加的商品编号已存在如何处理(假定数据不会输入重复类目编号)

解题思路

  • 使用map<int,set<pair<int,int> > > F保存所有数据,格式为map<Score,set<pair<Type,Commodity> > >
    这样在容器内部首先会按Score排序,同一Score的先按Type排序,再按Commodity排序。
  • 使用unordered_map<int,int>[50]保存编号到成绩的映射,格式为
    unordered_map<Commodity,Score> G[Type]
    这样指定Type和Commodity可以在F中迅速定位,从F中增删商品的时间度为O(log(Score的种数) * log(该分数的物品数))。

基础知识

  • map<class T1,class T2>特性:存储T1->T2映射的键值对,map先按照T1升序排序,再按T2升序排序,其中T1,T2可以是任意类(如:int、string、char或自定义类),T1值唯一,其插入删除的时间复杂度为O(log2)
  • unordered_map<class T1,class T2>特性:仅存储T1->T2映射的键值对,T1值唯一,其插入和删除的时间复杂度为O(1)

60分代码 2531ms/5000ms(疑似测试样例BUG,至今也没有想出到底是哪块出了问题。)

#pragma GCC optimize(2)
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#define DEBUG
#ifdef DEBUG
	#include<fstream>
	#include<ctime>
#endif
using namespace std;
int main(){
	#ifdef DEBUG
		clock_t t1=clock();
		ifstream cin("C:\\Users\\Isidore\\Desktop\\out.txt");
	#endif
	int m,n,op;
	ios_base::sync_with_stdio(false),cin.tie(NULL);
	unordered_map<int,int> G[51];//存储编号到分数的映射
	unordered_map<int,int>::iterator itG;
	map<int,set<pair<int,int> > > F;//map<Score,set<pair<Type,Commodity> > >
	map<int,set<pair<int,int> > >::iterator itF;
	cin>>m>>n;//初始化商品
	for(int i=0,id,score;i<n;++i){
		cin>>id>>score;
		for(int j=0;j<m;++j) F[score].insert(pair<int,int>(j,id)),G[j][id]=score;
	}
	cin>>op;//增删查操作
	for(int i=0,k,kind,type,commodity,score,K[51];i<op;++i){
		cin>>kind;
		switch(kind){
			case 1://增加
				cin>>type>>commodity>>score;
				G[type][commodity]=score;//在G中记录该Type类编号为Commodity商品成绩映射
				F[score].insert(pair<int,int>(type,commodity));//在F中插入该商品
				break;
			case 2://删除
				cin>>type>>commodity;
				if((itG=G[type].find(commodity))!=G[type].end()){//如果该商品存在则开始删除
					itF=F.find(itG->second);//首先查找该商品在F中找到所有Score为itG->second的所有商品
					itF->second.erase(pair<int,int>(type,commodity));//删除该商品
					if(itF->second.empty())F.erase(itF);//如果该分数的容器为空 删掉
					G[type].erase(itG);//删掉Type类的Commodity到Score的映射
				}
				break;
			case 3://查询
				cin>>k;//查询总个数k
				for(int j=0;j<m;++j) cin>>K[j];//输入每类不得超过的最大查询数
				set<int> S[51];//存储所有类查询到的商品编号
				for(auto it=F.rbegin();it!=F.rend()&&k>0;++it){//按分数由大到小选择前k个物品
					set<pair<int,int> >&s=it->second;//该分数下所有商品
					for(auto itS=s.begin();itS!=s.end()&&k>0;++itS){
						const pair<int,int>&p=*itS;
						if(K[p.first]>0) S[p.first].insert(p.second),--K[p.first],--k;//若该物品不超过选择数量则选择该物品
					}
				}
				for(int j=0;j<m;++j){//对所有类输出选择的物品
					if(S[j].empty())//该类没有选中任何物品
						cout<<-1<<endl;
					else{
						k=0;//仅仅用于判断是否输出空格
						for(auto it=S[j].begin();it!=S[j].end();++it,++k)
							cout<<(k==0?"":" ")<<*it;
						cout<<endl;
					}
				}
				break;
		}
	}
	#ifdef DEBUG
		clock_t t2=clock();
		::cout<<endl<<endl<<t2-t1<<"ms"<<endl;
	#endif
	return 0;
}

测试数据

//样例数据
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
//自测随机数据(没有灵魂)
#include<iostream>
#include<fstream>
#include<stdlib.h>
#include<map>
#include<time.h>
#include<set>
#include<iomanip>
#include<unordered_map>
#include<unordered_set>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
void output190904(){
	ofstream cout("out.txt");
	srand( time(NULL) );
	unordered_set<int> ids[50];
	int m=50,n=30000,id[30001],opnum=100000,oppask=100,kind;
	cout<<m<<" "<<n<<endl;
	for(int i=0;i<n;++i){
		id[i]=60000,cout<<i+i<<" "<<abs(rand()*rand())<<endl;
		for(int j=0;j<50;++j)ids[j].insert(i+i);
	}
	cout<<opnum<<endl;
	for(int i=0;i<opnum;++i){
		switch(rand()%3){
			case 0:
				kind=rand()%50;
				cout<<1<<" "<<kind<<" "<<(id[kind]+=2)<<" "<<abs(rand()*rand())<<endl;
				break;
			case 1:
				do{kind=rand()%50;}while(ids[kind].empty());
				cout<<2<<" "<<kind<<" "<<*ids[kind].begin()<<endl;
				ids[kind].erase(ids[kind].begin());
				break;
			case 2:
				if(oppask>1){
					--oppask;
					cout<<3<<" "<<rand()%101;
					for(int j=0;j<m;++j) 
						cout<<" "<<(rand()%100001);
					cout<<endl;
				}
				else
					--i;
				break;
		}
	}
	cout<<3<<" "<<rand()%101<<" ";
	for(int j=0;j<m;++j) cout<<" "<<rand()%100001;
} 
int main(){
	output190904();
	return 0;
}
发布了107 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/best335/article/details/102154998