题意:有m个种类,每个种类有n个物品。每个物品有自己的种类、编号、分数。要在所有物品中选出不超过sum个的物品。
按以下方式选择:①按分数排序,分数大的在前 ②分数相同的,种类小的在前 ③分数相同,种类相同,编号小的在前
每个种类可以选择的物品数量不得超过count[i]个,总的可以选择的物品数量不得超过sum个
每个种类一行,输出该种类所选择的物品的编号。如何该种类没有物品被选,则输出-1
有三种操作:①增加结点 ②删除结点 ③输出所选择的物品编号
注意:m<=50,n<=30000,但是id<=10^9。虽然物品的种类只有50种,每类物品的个数只有30000,但是物品的编号有10^9个。(编号并不是按物品的个数来计算的)
操作2只给type和id,所以需要映射。因为id<=1e9,所以用二维数组存a[type][id]=score是不可行的。要用unordered_map映射
思路:
①用 ID = type * 1e9 + id 唯一标识一个物品的编号,用set<Node> s存放所有的物品,在Node里定义 id, score, 以及set的排序方式。set会自动排序,所以不用sort一遍,否则会出错。
②用unordered_map<long long, set<Node>:: iterator> Map来存物品的ID和物品在set中的迭代器的映射,用来在删除set内部元素。向set增加元素时,用Map[ID]=s.insert(x).first来获取映射关系,时间复杂度O(1)。如果用遍历set的方法删除元素,O(n),会超时。
③用vector来存每个type所选的物品的id。set的结构体里没有存type,id,那么要如何获取type和id?
因为ID = type * 1e9 + id,所以type = ID / 1e9, id = ID % 1e9, 注意:因为ID是long long,所以设置const long long INF = 1e9
知识点:
*** insert函数的返回值是一个pair,first成员就是指向新插入的元素在set中位置的迭代器,second成员是一个bool表示这次插入操作是否成功
①关联容器包括:set、map
标准库提供set关联容器分为:
(1)按关键字有序保存元素:set(关键字即值,即只保存关键字的容器);multiset(关键字可重复出现的set);
(2)无序集合:unordered_set(用哈希函数组织的set);unordered_multiset(哈希组织的set,关键字可以重复出现)。
②在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。set中元素的值不能直接被改变。set内部采用的是一种非常高效的平衡检索二叉树:红黑树,也称为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树。
③set具备的特点:
(1)set中的元素都是排序好的
(2)set中的元素都是唯一的,没有重复的
④map:红黑树结构,自动排序
⑤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)
⑦unordered_map 无序映射:比map快(用map会超时)
⑧unordered_map 在C++ 11 才可以用,否则会编译出错
#include <bits/stdc++.h> #define MAX 51 using namespace std; struct Node{ long long id; int score; bool operator < (const Node &b) const{ //Set要内部排序,用struct写set,需要在struct内重写排序方法 if(score!=b.score) return score > b.score; else return id < b.id; } }; set <Node> s; //用set存放所有物品信息 vector<int> r[MAX]; unordered_map<long long, set<Node>::iterator> Map; //key:type*10^9 + id; value:物品在set中的迭代器 ||用type * 1e9 +id 唯一标识一个物品的序号 int m; //物品种类 int n; //物品个数 int sum; //总的不能超过的个数 int Count[MAX]; //每类物品不得超过的个数 const long long INF = 1e9; //因为ID是long long,并且要取模,所以要将1e9也设置为long long void op(){ int cont =0; for(int i=0;i<MAX;i++) r[i].clear(); for(set<Node>::iterator it =s.begin(); it!=s.end(); it++){ if(sum==0) break; Node a=*it; int type =a.id / 1e9; int id =a.id % INF; //long long % long long if(Count[type]>0){ //该种类还可以选 Count[type]--; r[type].push_back(id); sum--; } } for(int i=0;i<m;i++){ int len=r[i].size(); if(len!=0){ for(int j=0;j<len;j++){ if(j!=len-1) printf("%d ",r[i][j]); else printf("%d ",r[i][j]); } } else printf("-1 "); } } int main(){ int type,id,score,c,q; Node x; while(scanf("%d %d",&m,&n)!=EOF){ s.clear(); memset(Count,0,sizeof(Count)); for(int i=1;i<=n;i++){ scanf("%d %d",&id,&score); x.score=score; for(int j=0;j<m;j++){ x.id=j*1e9 + id; long long key=j*1e9+id; Map[key]=s.insert(x).first; } } scanf("%d",&q); for(int i=0;i<q;i++){ scanf("%d",&c); if(c==1){ scanf("%d %d %d",&type,&id,&score); x.id=type*1e9 + id; x.score=score; long long key=type*1e9+id; Map[key]=s.insert(x).first; //向Set插入元素 } else if(c==2){ scanf("%d %d",&type,&id); //set.erase()提供了4种重载,分别可以通过key,或者迭代器,或者迭代器范围来删除元素。 long long k=type*1e9+id; s.erase(Map[k]); //根据元素的迭代器,删除Set中的元素 Map.erase(k); //删除物品在Map中的映射 //s.erase(Node{type,id,score}); //为什么出错? } else if(c==3){ scanf("%d",&sum); for(int i=0;i<m;i++){ scanf("%d",&Count[i]); //第i个种类不得超过count[i]个 } op(); } } } return 0; }