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组成的环
化为链后就相当于有了一个数组(设为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;}
- ACM复习(14)8613 锁
- ACM复习(1)1077 韩信点兵
- ACM复习(2)1078 破密
- ACM复习(6)1144 数星星
- ACM复习(7)1079 三角形
- ACM复习(11)8615 快乐
- ACM复习(13)8612 发牌
- ACM复习(15)8614 素数
- [ACM] hdu 1465 不容易系列之一(错排复习)
- [ACM] POJ 1088 滑雪 (记忆化搜索复习)
- ACM复习(3)1139 约瑟夫环问题
- ACM复习(4)1142 巡逻的士兵
- ACM复习(5)1076 K尾相等数
- ACM复习(8)1143 多少个Fibonacci数
- ACM复习(9)8611 大牛之路I
- ACM复习(10)8619 公约公倍
- ACM复习(12)8618大牛之路II
- ACM复习(16)8617 阶乘数字和
- Linux系统安装与使用
- 168. Excel Sheet Column Title
- LCA(最近公共祖先)
- Kotlin to JavaScript(译)
- Lucene之中文庖丁解牛(mmseg)分词器-yellowcong
- ACM复习(14)8613 锁
- 认识jQuery
- 杭电OJ1002
- [Linux移植一]ubuntu搭建NFS
- MySQL学习笔记
- 机器学习之文本分类-神经网络TensorFlow实现(一)
- 团体程序设计天梯赛-练习集 L1-011. A-B
- Ubuntu安装MySQL及遇到的问题解决方案 xwj
- 二叉树面试题(一)