题意:
1403 有趣的堆栈
1.0 秒 131,072.0 KB 80 分 5级题
大家都熟悉堆栈操作。一个堆栈一般有两种操作,push和pop。假设所有操作都是合法的并且最终堆栈为空。我们可以有很多方法记录堆栈的操作,
(1) 对每个pop操作,我们记录它之前一共有多少个push操作。
(2) 对每个pop操作,我们记录这个被Pop的元素曾经被压上了几个。
例如:操作push, push, pop, push, push, pop, push, pop, pop, pop
用第一种方法 记录为 2, 4, 5, 5, 5
用第二种方法 记录为 0, 0, 0, 2, 4
这两种记录方法可以互相转化,我们的问题是,给定第二种记录方法的序列,请求出第一种记录方法的序列。
输入
第一行一个整数n,表示序列的长度(0 < n <=1000000)
第二行n个整数,表示第二种方法的记录。
输出
一行,空格分隔的n个整数,表示第一种表示方法的序列。
输入样例
5
0 0 0 2 4
输出样例
2 4 5 5 5
思路:
这是一个简单题。 其实把入栈对应为括号就理解了。示例对应如下括号序列
(()(()()))
第一种方法比较简单,就是记录每个右括号之前有少个左括号,如果我们有这个括号序列,直接扫一遍计数就可以了。
现在的问题变为如何从第二种表示方法的序列中还原出这个括号序列?
第二种表示方法实际上是看每个右括号包含了几对完整的括号。
例如输入0,0,0,2,4,我们来还原括号序列。我们用一个字符串来表示,我们用x表示未知的位置。首先我们知道这个字符串的长度是10,因为有5对括号的存在。
初始字符串: xxxxxxxxxx
我们从后往前扫描数列,也从后往前处理字符串。每次注意第一个遇到的未知的位置一定是右括号,我们根据数列信息,可以计算它对应的左括号的位置。
对于样例(注意每次从右往左第一个x就是右括号的位置)
(1) 处理4, 我们知道左括号和右括号间包含4对括号,所以是8个位置,更新字符串为: (xxxxxxxx)
(2) 处理2, 我们知道左括号和右括号间包含2对括号,所以是4个位置,更新字符串为: (xx(xxxx))
(3) 处理0, 我们知道左括号和右括号间包含0对括号,所以是挨着的,更新字符串为:(xx(xx()))
(4) 处理0, 我们知道左括号和右括号间包含0对括号,所以是挨着的,更新字符串为:(xx(()()))
(5) 处理0, 我们知道左括号和右括号间包含0对括号,所以是挨着的,更新字符串为:(()(()()))
至此,我们还原出这个括号字符串。还原这个序列其实就是从右往左扫一次字符串的过程。每次找到第一个没处理的x,更新为右括号,计算左括号的位置并设置左括号。
再从左到右扫一次可以还原出第一种方法的数列。
又被51nod的标签给坑了
代码实现:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<cstdlib>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e6 + 5;
const double pi = acos(-1.0);
int n;
int a[maxn];
int ans[maxn];
int vis[maxn];
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
}
int now = n;
for(int i = n * 2;i >= 1;i--){
if(ans[i] == 0){
ans[i - a[now] * 2 - 1] = 1;
vis[i] = 1;
now--;
if(now == 0) break;
}
}
for(int i = 1;i <= 2 * n;i++)
ans[i] = ans[i - 1] + ans[i];
for(int i = 1;i <= n * 2;i++){
if(vis[i] == 1){
printf("%d",ans[i]);
if(i == n * 2) printf("\n");
else printf(" ");
}
}
return 0;
}