[JSOI2010]缓存交换 贪心

大体思路:
我们把前m个数放到缓存区后,再碰到不在缓存区里的数时,我们需要删除一个原来在缓存区的数,然后把这个数加到缓存区内,首先问题在于删除那个数,我首先想到删除后面出现少的那个数,但这不对,反例如下:
1000 3
12 15 16 14 12 15 14 12 15 后面的数全是16
对于这个样例,如果对于14这个数,用上面的思路一定不删16,而实际上删16最划算,因为你不管删12还是15,在后面的几个数中你都要频繁的删数,而删16的话,紧跟着后面的这几个数不需要删除,后面的一坨16也只需要处理一次即可。
正确的贪心是,把缓存区中元素中下一次出现最晚的那个给删除了,所以我们需要一个数组来存第i个数的下一次出现在哪里,要一直找最晚的我们需要一个堆等等。

#include <bits/stdc++.h>

using namespace std;

int n, m;

//这个map是基于当前位置i
// 从n到1找ar[i]最后一次出现的位置(注意这个顺序n ~ 1)
// 即基于当前位置向后找第一次出现ar[i]的位置
map<int,int> mp;

bool boo[100050];//表示第i个数在不在缓存区内
int nexti[100050];//存第i个数下一次出现的位置
int ar[100050];
priority_queue<int> q;//用于存储缓存区内的元素
int num, ans;// num当前缓存区内元素个数,ans答案

int main()
{
    //n,主存访问次数,m容量
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d", &ar[i]);
    }
    // 从后向前遍历主存访问请求,找到每个元素最后一次出现的位置
    for(int i = n; i > 0; --i)
    {
        //对于每个元素,检查它是否在map mp中
        // 如果不在,说明这是该元素第一次出现,将nexti[i]设置为一个大值
        //表示该元素后面没有再次出现。
        if(mp[ar[i]] == 0)
            nexti[i] = 100050;
        //如果在,说明该元素之前已经出现过
        // 将nexti[i]设置为该元素最后一次出现的位置,即mp[ar[i]]。
        else    nexti[i] = mp[ar[i]];
        //更新map mp,将当前元素的索引i作为键,元素的值ar[i]作为值。
        mp[ar[i]] = i;
    }
    // 遍历主存访问请求
    for(int i = 1; i <= n; ++i)
    {
        if(boo[i] == 0)// 如果当前元素不在缓存区内
        {
            ans++; // 缺失次数加1
            if(num < m)// 如果缓存区未满
            {
                num++; // 缓存区内元素个数加1
                boo[nexti[i]] = 1; // 将当前元素标记为在缓存区内
                q.push(nexti[i]); // 将当前元素加入优先队列
            }
            else// 如果缓存区已满
            {
                boo[q.top()] = 0;// 移除缓存区内最久未使用的元素
                q.pop();
                boo[nexti[i]] = 1;// 将当前元素标记为在缓存区内
                q.push(nexti[i]);// 将当前元素加入优先队列
            }
        }
        else// 如果当前元素已经在缓存区内
        {
            boo[nexti[i]] = 1;// 将当前元素标记为在缓存区内
            q.push(nexti[i]);// 将当前元素加入优先队列
        }
    }
    printf("%d\n", ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值