更多题目请看CCF历年真题汇总
题目描述
输入
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 可以在nlogn 复杂度之内动态维护一个有序序列。所以用set<PII>维护每一类商品的{id, score}. (PII 是 pair<int,int>)
- score是从大到小小选,所以需要一个反向迭代器 set<PII>reverse_iterator
- 选择商品的时候,只需要在每一类开头选择分数最大的即可.
- 更多细节在代码中
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 55;
typedef pair<int,int> PII;
int n, m;
set<PII> g[N]; //第i类的score 和 id
set<PII>::reverse_iterator it[N];
map<int,int> f[N]; //删除的时候只有编号,所以要存储编号对应的分数,便于在集合里面删除元素
vector<int> ans[N]; //每个查询的答案。第i类选择了哪些
int main()
{
cin >> m >> n;
for(int i=0; i<n; i++)
{
int id, sc;
cin >> id >> sc;
for(int j=0; j<m; j++) {
g[j].insert({sc, -id}); //主要以分数排序,-id存储可以保证本身小的id在后面。可以被反向迭代器先遍历到。
f[j][id] = sc;
}
}
int q; cin >> q;
while(q --)
{
int op; cin >> op;
if(op == 1) //增加
{
int type, id, score;
// cin >> type >> id >> score;
scanf("%d%d%d", &type, &id, &score);
f[type][id] = score;
g[type].insert({score, -id});
}
else if(op == 2) //删除
{
int type, id;
// cin >> type >> id;
scanf("%d%d", &type, &id);
g[type].erase({f[type][id], -id});
f[type].erase(id);
}
else //查询
{
int tot;
cin >> tot;
int sum[N], cnt[N]; //第i类最多sum个,已经选了cnt个
/*
* 这个地方两个数组如果定义在这里,可能不会超时
* 如果定义的是全局变量,就大概率会超时。。。。
*
* 输入改成scanf不会超时!! 所有输入改成scanf好了。。。
*
*/
for(int i=0; i<m; i++) { //输入每个类别的数量限制
// cin >> sum[i];
scanf("%d", &sum[i]);
cnt[i] = 0; //清空数组
it[i] = g[i].rbegin(); //初始化反向迭代器
ans[i].clear();//初始化结果数组
}
while(tot--) //选不超过tot个
{
int k = -1;
for(int i=0; i<m; i++) //找到m类中当前最大的一个
{
if(it[i] != g[i].rend() && cnt[i] < sum[i]) //第i类还有商品,且数量没有达到限制
{
if(k == -1 || it[k]->first < it[i]->first) { //还没选或者比第k分数类更大
k = i;
}
}
}
if(k == -1) break; //什么都选不出来了
ans[k].push_back(-it[k]->second); //编号存的时候取反了,要变回来
it[k] ++; //第k类下一个
cnt[k] ++; //数量++
}
for(int i=0; i<m; i++) { //输出这次查询的答案
if(ans[i].empty()) puts("-1");
else {
for(auto x:ans[i]) {
cout << x << ' ';
}
cout << endl;
}
}
}
}
return 0;
}