Cooking Time Gym - 101498F (思维+贪心+优先队列)

题目链接:https://vjudge.net/problem/Gym-101498F

题目:
While cooking your dinner, you will need n ingredients. Initially, all ingredients are in the refrigerator.

You are not allowed to keep more than k ingredients outside the refrigerator at the same time. If there are k ingredients outside the refrigerator, and you need to use another ingredient from it, then you must do the following:

Open the refrigerator.
Return an ingredient that is currently outside.
Take the required ingredient.
Whenever you need an ingredient, you will open the refrigerator only if it’s not outside. Each time you open the refrigerator you can take only one item. Your task is to minimize the number of times you will need to open the refrigerator.

Input
The first line contains an integer T (1  ≤  T  ≤  100), where T is the number of test cases.

Each case contains two lines. The first line contains two integers n and k (1  ≤  n, k  ≤  105), the number of ingredients needed, and the maximum allowed number of ingredients that can be kept outside the refrigerator at the same time.

The second line contains n integers a1, a2, …, an (1  ≤  ai  ≤  109), where ai is the ID of the ith ingredient you will need. Ingredients must be used in the order given in the input.

Output
For each test case, print a single integer that represents the minimum number of times you will open the refrigerator.

Example
Input
2
5 3
2 4 5 2 1
7 3
1 2 3 4 3 2 1
Output
4
5
Note
In the first test case, you can keep up to 3 items outside the refrigerator. You must open the refrigerator 3 times to use the first three ingredients (2, 4, and 5), and then keep them outside the refrigerator. The fourth ingredient (2) is already outside the refrigerator, so you can use it directly. You cannot use the fifth item because there are 3 items outside the refrigerator, so you must open the refrigerator, return an item, and take the fifth ingredient from the refrigerator and use it.

题意: 有n种调料放在冰箱,你最多能把k种调料放外面,你每次打开冰箱,可以拿出一种调料,同时也可以放回去一种调料,也可以不放回。 每组输入案例第一行为n,k,第二行为n种调料的编号(从1到1e9),需要调料的顺序是输入的循序,输出是最少要打开多少次。

思路: 先求出每一个编号的下一个相同的编号的位置,每一个编号的最后一个数的下一个赋值为一个很大的编号,用优先队列储存放在外面的物品,当外面的物品达到了k个,此时某些情况我就得从队列中拿一个回去,而我优先队列是按next值的从大到小排列的,所以我拿回去的是符合贪心思维的。 详情见代码解析。

代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <string>
#include <stdlib.h>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>

using namespace std;

const int maxn =100000+5;
int a[maxn],next[maxn],last[maxn],inqueue[maxn];
//next数组下一个相同值在哪个位置;
//inqueue数组用来判断是否物品在在外面(即在队列中);

map<int, int> mp;//所有的id都编个号,就不要开1e9的数组了;

struct Node
{
    int next,id;
    bool operator <(const Node &rhs) const
    {
        return  next <rhs.next;//优先队列按next的值从大到小排列;
    }
};

int main()
{
    int T,n,k,ans;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        priority_queue<Node> Q;
        memset(inqueue,0,sizeof(inqueue));
        mp.clear();

        int cnt =0;
        scanf("%d%d",&n,&k);
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            if(!mp.count(a[i]))
                mp[a[i]]=cnt++;
            a[i]=mp[a[i]];//都重新编个号
            last[a[i]]=n;
        }//编号一定是小于n的 

        for(int i=n-1; i>=0; i--)  //反向遍历,找到它的下一个相同的位置,
        //因为前面所有的last都赋值为n,所以每一个不相同的编号的最后一个,它的next就为n
        {
            next[i]=last[a[i]];//为n的话,因为我的优先队列是按next值从大到小来排的,
            //所以当外面的物品达到了K个,我得从队列中(外面摆的物品)
            //拿一个物品回去,所以此时出队(即放回去对首的这个物品)是符合我的贪心思路的
            last[a[i]]= i;
        }

        for(int i=0; i<n; i++)
        {
            if(inqueue[a[i]])
            {
                Q.push((Node)
                {
                    next[i],a[i]
                });//如果要拿出的物品已经外面有了,那就把这个也压入队列,
                //因为next值发生了变化,也要更新。这是a[i]最新的next值
                continue;
            }

            if(k)
            {
                Q.push((Node)
                {
                    next[i],a[i]
                });
                inqueue[a[i]]=1;
                k--;
                ans++;
            }
            else
            {
                while(inqueue[Q.top().id]==0)// 把已经不在外面的物品但是在队列中
                                            //的物品清除。
                    Q.pop();

                Node x = Q.top();//把此时在外面的物品拿回去
                Q.pop();
                inqueue[x.id]=0;

                Q.push((Node)
                {
                    next[i],a[i]
                });//把新拿出来的物品压入队列
                inqueue[a[i]]=1;
                ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36300700/article/details/80113369