ACM复习(14)8613 锁

来源:互联网 发布:用友网络科技股份 编辑:程序博客网 时间:2024/06/07 14:41

Description
我们学校终于有自己的实验室啦!
老师让队员们布置一下实验室。Oyy接了个任务,由于实验室是我们共同的基地,所以打算把实验室的门锁改掉。改成让我们队员都能开,别人一律不能开:)
Oyy很快弄了一个神奇的锁,这个锁是这样的。先把校园卡靠近琐的感应器,锁就会自动生成N个正整数(2<=N<=100000)。这N个数组成一个环。譬如5个数字显示为如图1形式:

这里写图片描述

在显示出这些数字后1分钟内,输入答案密码就可以解锁。解锁密码Key的计算方法如下:
在这个环内选两个数,这两个数之和再加上这两个数在环内最短距离就是一个伪Key。最大的伪Key就是真正的解锁密码Key。
例如图2,选择26和15,从26逆时针走4步到15。则伪Key是26+15+4=45

这里写图片描述

但图3中,选择26,33,从26顺时针走3步到33。得到的伪Key是26+33+3=62。而且没有其他的伪Key比62大。所以Key为62。

这里写图片描述

Oyy很满意这个设计。因为他觉得除了我们的acm队员外,其他人是不能在1分钟内求出Key并输入的。你想成为acm队的一员吗?那得先“入门”:)

输入格式
输入包括多个Case,每个Case包含两行。第一行包括一个正整数N。表示环内数的个数。第二行包括n个正整数。所有正整数小于5000。最后以N为0为结束。

输出格式
每个Case包含1行输出。就是解锁Key。

输入样例
2
1 2
12
13 28 33 5 6 19 21 15 12 19 8 26
0

输出样例
4
62


解题思路

一般遇到环都是先化环为链,因为环中任意两数的距离d <= n / 2
所以将环展开为链后只需在链尾增加 n / 2个元素。
例:A, B, C, D组成的环 => A, B, C, D, A, B

化为链后就相当于有了一个数组(设为num),数组中每个元素有一个index,按照题目要求
取num[i] 和 num[j] ,此时有:

key = num[i] + num[j] + abs(i - j), i != j && abs(i - j) <= n / 2

很容易想到的是暴力搜索,但 2<=N<=100000, 暴力搜索肯定超时。

仔细观察伪key生成公式,如果我们假设每一次取数时都有 i > j, 那么公式可以化简为

key = num[i] + num[j] + i - j = num[i] + i + (num[j] - j), i - j <= n / 2

此时我们可以建立一个num[ j ] - j 的优先队列,大的放在队头,每次选定 i 之后只需从队列
头开始匹配,直到找到 这样一个 j, i - j <= n / 2(因为还是环的时候任意两数最大距离为
n / 2),此时的 j 以及对应的 num[ j ]能和 i 以及num[ i ]组成最大的伪key

最后说一下这题遇到的两个坑点
1. 刚开始做这道题天真的以为可以先建好队列然后再遍历num数组就可以很快得出结果,
然而我忽略了直接建立一个完整队列需要的时间,用qsort也不行。
2. 第二点也是最最最最坑的,这道题不能用c++来做,不然肯定超时。

下面先展示两个超时版本最后一个才是正确版本,读者可以对比一下c++超时版本和正确
版本的区别。

//天真超时版本:#include<stdio.h>#include<stdlib.h>int cmp(const void *a, const void *b);struct prio{    int index;    int value;}queue[100002];int num[150000];int main(){    int n, index, t, max;    while(scanf("%d", &n) && n)    {        max = 0;        for(int i = 0; i < n; i ++)            scanf("%d", &num[i]);        for(int i = n; i < n + n / 2; i ++)            num[i] = num[i - n];        for(int i = 0; i < n + n / 2; i ++)        {            queue[i].index = i;            queue[i].value = num[i];        }        qsort(queue, n + n / 2, sizeof(prio), cmp);        for(int i = 1; i < n + n / 2; i ++)        {            index = 0;            while(1)            {                if(queue[index].index < i && i - queue[index].index <= n / 2)                    break;                index ++;            }            t = num[i] + i + queue[index].value - queue[index].index;            max = max > t ? max : t;        }        printf("%d\n", max);    }}int cmp(const void *a, const void *b){    prio * a2 = (prio *)a;    prio * b2 = (prio *)b;    if(a2->value - a2->index > b2->value - b2->index)        return -1;    else         return 1;}
// c++超时版本#include<iostream>using namespace std;struct priority{    int index;    int value;}queue[100002];int num[150004];int getIndex(int head,int rear,int num);int main(){    int n, head, rear, max, t, i;    while(cin >> n && n != 0)    {        max = 0;        head = rear = 1;        for(i = 1; i <= n; i ++)            cin >> num[i];        for(; i <= n + n / 2; i ++)            num[i] = num[i - n];        queue[head].index = 1;        queue[head].value = num[1];        for(i = 2; i <= n + n / 2; i ++)        {            while(i - queue[head].index > n / 2)                head ++;            t = num[i] + i + queue[head].value - queue[head].index;            max = max > t ? max : t;            rear = getIndex(head, rear, num[i] - i);            queue[rear].value = num[i];            queue[rear].index = i;        }        cout << max << endl;    }    return 0;}int getIndex(int head,int rear,int num){    int m;    while(head <= rear)    {        m = (head + rear) / 2;        if(queue[m].value - queue[m].index == num)        return m;        else if(queue[m].value - queue[m].index < num)        rear = m - 1;        else        head = m + 1;    }    return head;}
//正确版本#include<stdio.h>struct priority{    int index;    int value;}queue[100002];int num[150004];int getIndex(int head,int rear,int num);int main(){    int n, head, rear, max, t;    while(scanf("%d", &n) && n != 0)    {        max = 0;        head = rear = 1;        for(int i = 1; i <= n; i ++)            scanf("%d", &num[i]);        for(int i = n + 1; i <= n + n / 2; i ++)            num[i] = num[i - n];        // 只需初始化队头元素        queue[head].index = 1;        queue[head].value = num[1];        for(int i = 2; i <= n + n / 2; i ++)        {            /*                这里可以直接把queue[head]删掉是因为i是递增的               如果对于当前 i 有 i - queue[head].index > n / 2               那么对于后面的 i 必定也有 i - queue[head].index > n / 2            */            while(i - queue[head].index > n / 2)                head ++;            t = num[i] + i + queue[head].value - queue[head].index;            max = max > t ? max : t;            /*                更新队列,即将当前 i 的 num[i] - i 加入到队列                这里可直接替换队列中的queue[rear]是因为当前 i 离                后面的 i 更近并且具有相等的或更大的 num[i] - i(相当于                num[j] - j            */            rear = getIndex(head, rear, num[i] - i);            queue[rear].value = num[i];            queue[rear].index = i;        }        printf("%d\n", max);    }    return 0;}int getIndex(int head,int rear,int num){    int m;    while(head <= rear)    {        m = (head + rear) / 2;        if(queue[m].value - queue[m].index == num)        return m;        else if(queue[m].value - queue[m].index < num)        rear = m - 1;        else        head = m + 1;    }    return head;}
原创粉丝点击