腾讯2020校园招聘----逛街
文章目录
一、问题描述
小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。
小Q从第一栋一直走到了最后一栋,小Q从来都没有见到这么多的楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住)
输入描述:
输入第一行将包含一个数字n,代表楼的栋数,接下来的一行将包含n个数字wi(1<=i<=n),
代表每一栋楼的高度。
1<=n<=100000;
1<=wi<=100000;
输出描述:
输出一行,包含空格分割的n个数字vi,分别代表小Q在第i栋楼时能看到的楼的数量。
输入例子1:
6
5 3 8 3 2 5
输出例子1:
3 3 5 4 4 4
例子说明1:
当小Q处于位置3时,他可以向前看到位置2,1处的楼,向后看到位置4,6处的楼,加上第3
栋楼,共可看到5栋楼。当小Q处于位置4时,他可以向前看到位置3处的楼,向后看到位置
5,6处的楼,加上第4栋楼,共可看到4栋楼。
二、问题分析
这题比较简单,属于单调栈类问题;
单调栈
直接看代码:
方法一:暴力解法(超时)
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int main()
{
int n;
while(cin>>n)
{
vector<int> arr(n);
for(int i = 0;i < n;i++)
{
cin>>arr[i];
}
for(int i = 0;i < n;i++)
{
stack<int> s1;
stack<int> s2;
for(int j = i - 1;j >= 0;j--)
{
if(s1.empty() || s1.top() < arr[j])
s1.push(arr[j]);
}
for(int j = i + 1;j < n;j++)
{
if(s2.empty() || s2.top() < arr[j])
s2.push(arr[j]);
}
cout<<s1.size() + s2.size() + 1<<" ";
}
}
return 0;
}
暴力的方法是遍历数组中的每个元素,对每个元素的左边和右边分别根据单调栈的性质把元素放进两个栈中,最后返回两个栈中的元素个数之和+1;
这就相当于对每个元素都有n的复杂度,整个就接近 ,复杂度太高
方法二、优化
- 既然复杂度太高,我们就一趟从前往后遍历找到所有位置满足情况的元素的个数
- 再一趟从后往前遍历找到所有位置满足情况的元素的个数
- 最后把二者相加就是结果
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> arr(n);
vector<int> dp(n);//保存从前往后和从后往前遍历的结果
for (int i = 0; i < n; i++)
cin >> arr[i];
stack<int> l,r;//单调栈
//从后往前遍历,找到每个位置后面能看到的房子个数
for(int i = n-1;i > 0;i--)
{
//如果栈顶元素比当前元素小,说明我这个高个子挡住后面矮个子的房子了
//那么从当前位置往后面看,肯定就看不到矮个子的房子,只看到高个子的房子
//所以可以直接pop掉矮个子的房子
while (!r.empty() && arr[i] >= r.top())
r.pop();
r.push(arr[i]);
dp[i - 1] = r.size();
}
//注意边界问题:从后往前没有计算到0,在1的时候就把0计算了
cout<<dp[0] + 1<<" ";
//从后往前遍历,找到每个位置前面能看到的房子个数
for(int i = 0;i < n-1;i++)
{
//同理
while (!l.empty() && arr[i] >= l.top())
l.pop();
l.push(arr[i]);
dp[i + 1] += l.size() + 1;
cout<<dp[i + 1]<<" ";
}
}