PAT 1057
Stack is one of the most fundamental data structures, which is based on the principle of Last In First Out (LIFO). The basic operations include Push (inserting an element onto the top position) and Pop (deleting the top element). Now you are supposed to implement a stack with an extra operation: PeekMedian -- return the median value of all the elements in the stack. With N elements, the median value is defined to be the (N/2)-th smallest element if N is even, or ((N+1)/2)-th if N is odd.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (<= 105). Then N lines follow, each contains a command in one of the following 3 formats:
Push key
Pop
PeekMedian
where key is a positive integer no more than 105.
Output Specification:
For each Push command, insert key into the stack and output nothing. For each Pop or PeekMedian command, print in a line the corresponding returned value. If the command is invalid, print "Invalid" instead.
Sample Input:
17
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
PeekMedian
Pop
Pop
Pop
Pop
Sample Output:
Invalid
Invalid
3
2
2
1
2
4
4
5
3
Invalid
算法思路
本题考查的是动态中位数的算法, 并且注意到这题时间限制是150ms,意味着至少得使用\(O(n)\)的算法,否则一定
关于中位数
当数组长度为N, 是中位数一般被定义为$ (N+1)/2 $,找中位数的算法简单想了一下, 有以下几种
- 排序后取[(N + 1)/2]的值,几乎是万能的,即使是频繁更新这个方法也适用,但是复杂度比较高,\(\log_{2}N\), 每次都需要排序,这题是行不通的
- 网上看到有使用两个堆, 一个大根堆, 一个小根堆,大根堆存小于中位数的数, 小根堆存大于中位数的数,因此只要保持两个堆的数字数量相差不超过1, 那么大根堆的堆顶就是中位数。然后初始化一个中位数,每当插入一个数的时候,若大于这个中位数,插入小根堆, 若小于中位数,插入大根堆,接着如果不满足数量平衡,再从一个堆的顶部拿出来一个数加入另一个堆即可。
分析:插入操作复杂度为\(O(\log N)\), 调整也是\(O(\log N)\),取出也是\(O(\log N)\),因此速度还是可以的,如果需要执行pop,可以从堆中删除一个数,复杂度同样也是\(O(\log N)\) - 这里看到一种新的数据结构:树状数组,参考网上众多博客后发现这篇 搞懂树状数组 写的比较清楚,先mark一下,需要熟悉一下树状数组的基本思想才能理解以后更好的运用,希望以后自己也能总结出一篇这样的文章来。
回到题目
总结一下这题的算法, 使用树状数组的最直接的好处是在频繁更新的数组中很容易的计算出一个连续区间[1,n]的和, 有了这个性质, 当然也可以计算出任意区间[m, n]的和, 只需要减一下就行了, 计算和插入速度都是log的,那么如何使用这个性质找到中位数呢?
首先我们有这么几个数:
| A | 1 | 1 | 2 | 3 | 4 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|---|
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
可以得到一个count数组
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| count | 0 | 2 | 1 | 1 | 2 | 1 | 1 |
现在计算sum(0, 4) = 4就能知道数组中小于等于4的整数有(2 + 1 + 1 + 1) = 6个, 我们知道如果这个答案等于(N+1)/2这个index就是我们需要的中位数,但是我们需要从0开始遍历每一个index让后求出sum(0, index)来找到sum=4的位置吗, 那么最坏情况是要找n次。好在sum数组有一个很好的性质——非递减数组,也就是sum(i)天生就是排好序的(当然这只适用于没有负数的情况),我们可以使用一个二分查找很快的找到sum(i) = 4 的i于是就是这题的思路了。
PS:在二分查找的时候会遇到一些问题,这里有一个例子
| A | 1 | 1 | 2 | 3 | 5 | 6 | 100 | 100 |
|---|---|---|---|---|---|---|---|---|
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ... | 100 |
|---|---|---|---|---|---|---|---|---|---|
| count | 0 | 2 | 1 | 1 | 0 | 1 | 1 | ... | 2 |
这样算出来sum(0, 3) 和sum(0, 4)都等于4, 然而我们只需要的是最左边的4(很显然, 4并没有存在于我们的数组),因此有一个必须做到的是必须用binsearch找到最左边的4,这里需要注意,看完这篇博客二分查找技巧以后, 感觉自己没学过二分查找似的,还是要努力学习:
最后上源码:
#include <stdio.h>
#include <string.h>
using namespace std;
const int MAX = 100005;
int cnt[MAX];
int N;
int stack[MAX];
int len = -1;
void init()
{
memset(cnt, 0, sizeof(cnt));
}
void add(int pos, int delt)
{
while(pos <= MAX)
{
cnt[pos] += delt;
pos += pos&-pos;
}
}
int sum(int i)
{
int s = 0;
while(i)
{
s += cnt[i];
i -= i&-i;
}
return s;
}
void push()
{
int key;
scanf("%d", &key);
stack[++len] = key;
add(key, 1);
}
void pop()
{
if(len == -1)
{
printf("Invalid\n");
return;
}
int key = stack[len];
len--;
add(key, -1);
printf("%d\n", key);
}
int binsearch(int s)
{
int l = 0, r = MAX - 1;
int mid;
while(l <= r)
{
mid = ((l + r) / 2);
int k = sum(mid);
if(s <= k)
{
r = mid - 1;
}
else{
l = mid + 1;
}
}
return l;
}
void mid()
{
if(len == -1)
{
printf("Invalid\n");
return;
}
printf("%d\n", binsearch((len + 2) / 2));
}
int main()
{
scanf("%d", &N);
init();
while(N--)
{
char cmd[16];
scanf("%s", cmd);
switch(cmd[1])
{
case 'u':push();break;
case 'o':pop();break;
case 'e':mid();break;
}
}
return 0;
}
最后补一句
其实这是改良过的代码,原来使用STL中的stack作为存储的容器,然后使用cin读取,好几个点没过去,后来换了自己写的stack,然后改成scanf和printf以后就过去了,看来cin和scanf速度差别还是挺大的,STL也要慎用
PAT 1057的更多相关文章
- PAT 1057. Stack (30)
题目地址:http://pat.zju.edu.cn/contests/pat-a-practise/1057 用树状数组和二分搜索解决,对于这种对时间复杂度要求高的题目,用C的输入输出显然更好 #i ...
- PAT 1057 数零壹 (20)(代码+思路)
1057 数零壹(20 分) 给定一串长度不超过 105 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一下 N 的二 ...
- PAT——1057. 数零壹
给定一串长度不超过105的字符串,本题要求你将其中所有英文字母的序号(字母a-z对应序号1-26,不分大小写)相加,得到整数N,然后再分析一下N的二进制表示中有多少0.多少1.例如给定字符串“PAT ...
- PAT 1057 数零壹
https://pintia.cn/problem-sets/994805260223102976/problems/994805270914383872 给定一串长度不超过 105 的字符串, ...
- PAT 1057 Stack [难][树状数组]
1057 Stack (30)(30 分) Stack is one of the most fundamental data structures, which is based on the pr ...
- PAT 1057. 数零壹(20)
给定一串长度不超过105的字符串,本题要求你将其中所有英文字母的序号(字母a-z对应序号1-26,不分大小写)相加,得到整数N,然后再分析一下N的二进制表示中有多少0.多少1.例如给定字符串“PAT ...
- PAT 1057. Stack
Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...
- PAT甲级1057. Stack
PAT甲级1057. Stack 题意: 堆栈是最基础的数据结构之一,它基于"先进先出"(LIFO)的原理.基本操作包括Push(将元素插入顶部位置)和Pop(删除顶部元素).现在 ...
- PAT Basic 1057
1057 数零壹 给定一串长度不超过 105 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一下 N 的二进制表示中有 ...
随机推荐
- 第十九篇 js高级知识---词法分析和AO 链
上面一篇文章说了js的作用域链,这一节算是对上面的延申,有一个典型的例子,首先看原来的一段代码: var name = "test"; function t() { var b = ...
- 细谈position属性:static、fixed、relative与absolute
学习WEB有些时日了,对DOM中的定位概念有些模糊,特地花了一个下午的时间搜资料.整理写下这篇随笔. 首先,我们要清楚一个概念:文档流. 简单的讲,就是窗体自上而下分成一行一行,并在每行中按照从左到右 ...
- 一道面试题引发的对javascript类型转换的思考
最近群里有人发了下面这题:实现一个函数,运算结果可以满足如下预期结果: add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15 对 ...
- input 即时搜索 监听输入值的变化
在 Web 开发中经常会碰到需要动态监听输入框值变化的情况,如果使用 onkeydown.onkeypress.onkeyup 这个几个键盘事件来监测的话,监听不了右键的复制.剪贴和粘贴这些操作,处理 ...
- [No0000C1]Excel 删除空白行和空白列VBA代码
在exce中删除空行和空列的方法有很多,相对而言删除空行较为简单,只需进行筛选,将空白行筛选出来,删除即可,但要删除空列比较困难.因为你不能按列进行筛选删除.Excel中没有这个功能.当然你可以用另外 ...
- 记录Winform开发过程中遇到的情况
前两天开发了个Winform操作Excel和数据库的一个小程序,把Winform的一些东西又给捡了起来,当中又学到了一些新的东西,特来写出来留作纪念. 一.CSKIN美化框架的使用 刚开始做的时候,发 ...
- spring+mybatis之声明式事务管理初识(小实例)
前几篇的文章都只是初步学习spring和mybatis框架,所写的实例也都非常简单,所进行的数据访问控制也都很简单,没有加入事务管理.这篇文章将初步接触事务管理. 1.事务管理 理解事务管理之前,先通 ...
- ajax使用及代码表示
最近学习了ajax,记录一下学习写过的代码和一些问题 一.原生ajax var xhr = null; if(window.XMLHttpRequest) { xhr = new XMLHttpReq ...
- JS——控制标记的样式
1.定义一个div,宽度为100px,高度为100px,背景色为粉色. 定义一个事件,鼠标移入时背景色变为蓝色,宽度变为200px. 定义一个事件,鼠标移出时背景色变为红色. html文件: < ...
- C#研究OpenXML之路(2-DocumentFormat.OpenXml命名空间)
一.OpenXML对象结构预览 昨天感受了一下OpenXML的编程,今天开始准备一头扎进OpenXML了.在了解一门新的知识前,首先最重要的是理清逻辑结构,否则学习起来会感觉摸不着北. 1.首先打开V ...