题目链接https://pintia.cn/problem-sets/994805342720868352/problems/994805348471259136
题目大意:访问次数N
,每次访问一个商品(序号用从1
开始的整数开始),推荐顺序按照访问次数多和商品序号小来排序。每次访问,给出在本次访问之前的前K
个推荐的商品,若商品访问次数为0
则不输出(因此从第二次访问开始才有输出)。
开始的思路是用一个bool
的table来表示,table[i][j] == true
表示商品j
被至少访问了i
次。随后i
从后往前扫描即可。但query次数最多有N <= 50000
次,发现超内存了。。。
随后想到大顶堆(maxHeap),但发现【访问次数相同】但【序号大小不同】的商品【如果在一个父亲节点的两边】的话,是无法保证序号小的商品被放到根节点的。。。写Heap还花了好多时间结果也没AC
看了柳神的答案,是用重载STL的set
的运算符做的,简洁是非常简洁,但感觉不是用算法而是利用了STL的特性完成的(当然这也很好,是我不熟悉STL可以这样用…),于是还是想找个算法来做出这题。
后来看了一些答案后,有了思路:
虽然query次数N
可能很大,但是需要展示(维护)的推荐序列长度K
却不大(K<=10
),所以维护一个长度为11
的推荐序列rec[]
就好。
每一次访问输出后,本次query的商品item
计数增加1
。如果item
不在rec[]
中的话:
- 如果
rec[]
未满,将item
塞入rec[]
尾部。如果此时满了,置isFull = true
- 如果
rec[]
满了,和最后rec[]
最后一个比,如果item
更值得推荐,就将其替换
acc[item]++;
bool isIn = false;
for (int j = 1; j <= K; j++) {
if (rec[j] == item) {
isIn = true;
break;
}
}
if (isIn == false) {
if (isFull == false) {
rec[pos++] = item;
if (pos == K)
isFull = true;
}
else {
if (acc[item] > acc[rec[K]])
rec[K] = item;
else if (acc[item] == acc[rec[K]])
rec[K] = min(item, rec[K]);
}
}
【注意!!!】为什么和最后一个比就能保证rec[]
序列正确呢?因为在每次输出前我们都会对rec[]
排序,因此最后一个元素就是推荐度最低的。而每次query的item
最多替换掉rec[]
里一个元素,那么如果替换掉,就必然是替换这最后一个元素。
if (i != 0) {
printf("%d:", item);
sort(rec+1, rec+1+K, cmp);
for (int j = 1; j <= K; j++) {
if (rec[j] != 0)
printf(" %d", rec[j]);
else
break;
}
printf("\n");
}
那么就算情况最坏,也只是对长度为10
的数组排序50000
次,时间是足够的。AC成功!
完整代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
using namespace std;
int acc[50001];
int rec[11];
bool cmp(int x, int y) {
if (acc[x] != acc[y])
return acc[x] > acc[y];
else
return x < y;
}
int main() {
int N, K, max_acc = 0;
scanf("%d %d", &N, &K);
int isFull = false;
int pos = 1;
acc[0] = -1;
for (int i = 0; i < N; i++) {
int item;
scanf("%d", &item);
if (i != 0) {
printf("%d:", item);
sort(rec+1, rec+1+K, cmp);
for (int j = 1; j <= K; j++) {
if (rec[j] != 0)
printf(" %d", rec[j]);
else
break;
}
printf("\n");
}
acc[item]++;
bool isIn = false;
for (int j = 1; j <= K; j++) {
if (rec[j] == item) {
isIn = true;
break;
}
}
if (isIn == false) {
if (isFull == false) {
rec[pos++] = item;
if (pos == K)
isFull = true;
}
else {
if (acc[item] > acc[rec[K]])
rec[K] = item;
else if (acc[item] == acc[rec[K]])
rec[K] = min(item, rec[K]);
}
}
}
return 0;
}