2017京东面试编程题 - 保卫方案
- 来自codeforce的原题:
题意:
就是在一个环形的地方,排列着一堆山,要求有几对山是可以相互观察的对方的。
相互观察的条件是
- 如果相邻,必可以互相观察
- 不相邻,但是两者之间没有更高的山,可以互相观察,因为环形的,所以有两个方向,顺时针和逆时针,只要有一个方向可以看到就算可以互相观察。
思路:
我一开始用最简单的暴力,向两边暴力枚举,复杂度差不多是O(n^2),不然是超时的。
看了别人题解发现可以用dp做,复杂度是O(n)。
思路可以说是比较牛批了:
-
首先要拆环成链,将山的序列改变,第一座山是最高的山。如果第一座是最高的山,那么对于这个环形来说,是不可能通过穿过第一座山去找到可以相互观察的山了,这样就只用讨论正方向的情况了。
-
其次是统计对于这个序列的L数组和R数组。表示:
First hill to the left of the x, which is strictly higher than x.
First hill to the right of the x, which is strictly higher than x.
- 生成C数组:
All hills that are as high as x and are located between x and y.
- 注意left和right统计两次的情况,比如第二高的山在中间的时候
ac代码
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e6 + 5;
int a[maxn], b[maxn], L[maxn], R[maxn], C[maxn];
int n;
int main()
{
cin >> n; //输入山的数量
int ma = -1, mid = 0; //用于把a[]转化成最高山在第一位数组b[]的临时变量
for (int i = 0; i < n; i++) //输入a
{
cin >> a[i];
if (a[i] > ma)
{
ma = a[i];
mid = i;
}
}
mid--;
for (int j = 1; j <= n; j++) //将a[]转化成最高的山在第一位的b[],最高的山在b[1]
{
b[j] = a[(mid + j) % n];
}
L[1] = 1; //left数组中设定最高的山,下一个比他高的设为1,即自己
for (int i = 2; i <= n; i++) //生成left数组
{
L[i] = i - 1; //设定左边的第一座山就比自己高
while (L[i] > 1 && b[L[i]] <= b[i]) //while语句左移直到找到比自己要高的山
L[i] = L[L[i]];
}
for (int i = n; i >= 1; i--) //生成right,C数组
{
R[i] = i + 1; //设定右边第一座山就比自己高,并且设定右边的山默认是最高的,因为和最高的山相邻
while (R[i] <= n && b[R[i]] < b[i]) //while语句右移知道找到跟自己相等或者比自己高的山
R[i] = R[R[i]];
if (R[i] <= n && b[R[i]] == b[i]) //如果跟自己一样高,则C[]++
{
C[i] = C[R[i]] + 1;
R[i] = R[R[i]];
}
}
long long ans = 0; //结果可能很大,用longlong存储
for (int i = 2; i <= n; i++) //不用计算最高的山
{
ans += C[i] + 2;
if (L[i] == 1 && R[i] == n + 1) //此时就是和最高的山形成pair,重复计算了,所以减1
{
ans--;
}
}
cout << ans << endl;
return 0;
}