个人练习-PAT甲级-1129 Recommendation System

题目链接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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值