sjtu online judge 1034 二哥的金链

一道比较有意思的题目,我把队列改造了一下然后ac了,后来学数据结构的时候发现我改的队列还有个名词,叫单调队列,心里颇为激动,于是把题目和我的代码发上来与大家分享一下。


Description

一个阳光明媚的周末,二哥出去游山玩水,然而粗心的二哥在路上把钱包弄丢了。傍晚时分二哥来到了一家小旅店,他翻便全身的口袋也没翻着多少钱,而他身上唯一值钱的就是一条漂亮的金链。这条金链散发着奇异的光泽,据说戴上它能保佑考试门门不挂,RP++。好心的老板很同情二哥的遭遇,同意二哥用这条金链来结帐。虽然二哥很舍不得这条金链,但是他必须用它来付一晚上的房钱了。

金链是环状的,一共有 N 节,老板的要价是 K 节。随便取下其中 K 节自然没问题,然而金链上每一节的 RP 值其实并不一样,有高有低,二哥自己非常清楚。另外二哥并不希望把整个金链都拆散了,他只愿意在这条环形的金链上切两刀,从而切下一段恰好为 K 节的金链给老板。因为 RP 值越高的节越稀有,因此他希望给老板的金链上最高的 RP 值最小。

Input Format

第一行两个整数 N 和 K,表示金项链有 N 节,老板要价 K 节。

第二行用空格隔开的N个正整数 a1...aN ,表示每一节金链的价值为多少。

Output Format

输出一个整数,表示二哥给老板的金链上最高的 RP 值最小多少。

Sample Input

5 2
1 2 3 4 5

Sample Output

2

Sample Input

6 3
1 4 7 2 8 3

Sample Output

4

说明

对40%的数据, 3N200  ;

对70%的数据, 3N20000

对100%的数据, 3N200000  ,  0<ai109

数据规模较大,建议用scanf("%d", &a[i]);来读数据。


说的简单点,就是n个数组成一个圆环,每相邻的k个数中会有一个最大值,一共有n组最大值,然后找出n个最大值中的最小的那个。

直接暴力算的话,对于每个k个数的组,扫一遍可找到最大值,n组每组扫一遍,时间复杂度O(k*n)。这显然不是最理想的,因为相邻的两个组之间,共有的数被重复检查。

考虑维护一个队列,队列中有k个元素,一开始可初始化为圆环的前k个元素,此时,可以扫一遍找出当前队列中的最大值,然后不断地将队首元素出队,队尾则将圆环上新的数入队,然后维护队列的最大值,每次更新队列之后将新的最大值与已经获得的最大值比较并取小的即可。

然后发现一个事实:由于我们关心的是队列的最大值,所以当队列中进入一个较大的元素时,可以舍弃掉原队列中的较小元素。假设队列中原有元素为a1,a2,...,am, 新入队元素为b,则可以从队尾向前搜索,将所有比b小的元素出队,然后将b入队。这样维护的话,整个队列将是单调递减的,所以每次询问最大值的时候直接返回队首元素即可!

另外这样做的话,队列不能记忆那些较小的元素,所以在出队的时候需要知道让哪个数出队,假设我们要让b出队,则队首元素要么比b大,要么等于b!(因为b曾经在队列中,所以比b小的已经在b入队时被挤出队了)若队首元素大于b,则不操作,若队首元素等于b,则队首出队即可。

表面上看,每次入队的时候似乎要从后往前扫一遍队列,即入队时间复杂度O(n),其实不然,若某次入队将较多的元素挤出队,则队列的规模将减小。实际上,对于每个元素,无非就是1次入队,1次出队(被挤出队或是正常出队),从均摊的角度看,入队时间为O(1)!另显然出队操作和询问队列最大值也是O(1),在建队时有k次入队,此后有n次询问、出队和入队,总时间复杂度为O(n+k),这是比较理想的。

以下是我的代码,欢迎高手来拍砖指点。

#include <iostream>
#include <cstdio>

using namespace std;

class monotoneQue
{
    int *data;
    int front;
    int rear;
    int maxsize;
public:
    monotoneQue(int initsize=10):front(0),rear(0),maxsize(initsize)
    {
        data=new int[maxsize];
    }
    ~monotoneQue(){delete [] data;}
    void enqueue(int x)
    {
        while(rear!=front&&data[(rear-1)%maxsize]<x)
        {
            rear=(rear-1)%maxsize;
        }
        data[rear]=x;
        rear=(rear+1)%maxsize;
    }
    void dequeue(int x)
    {
        if(data[front]==x)
        {
            front=(front+1)%maxsize;
        }
    }
    bool isempty(){return front==rear;}
    int head(){return data[front];}
};

monotoneQue que(200000);

int main()
{
    int n,k;
    cin>>n>>k;
    int rp[n];
    for(int i=0;i<n;++i)
    {
        scanf("%d",&rp[i]);
    }

    int min_temp;
    for(int i=0;i<k;++i)
    {
        que.enqueue(rp[i]);
    }
    min_temp=que.head();
    if(n==k)
    {
        cout<<min_temp<<endl;
        return 0;
    }
    int p=k;
    while(p!=k-1)
    {
        que.dequeue((p-k<0)?(rp[p-k+n]):(rp[p-k]));
        que.enqueue(rp[p]);
        if(min_temp>que.head())
        {
            min_temp=que.head();
        }
        //cout<<min_temp<<" "<<que.head()<<" "<<p<<endl;
        p=((p+1==n)?(0):(p+1));
    }

    cout<<min_temp<<endl;

    return 0;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值