保衛方案
題目描述
鏈接:https://www.nowcoder.com/questionTerminal/e1967ae812ea42e7a3ce57ee1f83b686
來源:牛客網
戰爭游戲的至關重要環節就要到來了,這次的結果將決定王國的生死存亡,小B負責首都的防衛工作。首都位於一個四面環山的盆地中,周圍的n個小山構成一個環,作為預警措施,小B計划在每個小山上設置一個觀察哨,日夜不停的瞭望周圍發生的情況。
一旦發生外地入侵事件,山頂上的崗哨將點燃烽煙,若兩個崗哨所在的山峰之間沒有更高的山峰遮擋且兩者之間有相連通路,則崗哨可以觀察到另一個山峰上的烽煙是否點燃。由於小山處於環上,任意兩個小山之間存在兩個不同的連接通路。滿足上述不遮擋的條件下,一座山峰上崗哨點燃的烽煙至少可以通過一條通路被另一端觀察到。對於任意相鄰的崗哨,一端的崗哨一定可以發現一端點燃的烽煙。 小B設計的這種保衛方案的一個重要特性是能夠觀測到對方烽煙的崗哨對的數量,她希望你能夠幫她解決這個問題。
輸入描述
輸入中有多組測試數據,每一組測試數據的第一行為一個整數n(3<=n<=10^6),為首都周圍的小山數量,第二行為n個整數,依次表示為小山的高度h(1<=h<=10^9).
輸出描述
對每組測試數據,在單獨的一行中輸出能相互觀察到的崗哨的對數。
示例1
輸入
5
1 2 4 5
輸出
7
求解思路
這個題目拿到之后看了好久不明白意思,翻看了別人的代碼,在網上找了一些博客,在最終弄懂了,比較有用的博客鏈接為左神面試算法整理---單調棧, 原作者禁止轉載和評論。
此題的關鍵之處在於:任意兩個山峰\(A\)和\(B\), 若滿足中間的任意山峰\(C\)的高度都低於\(A\)和\(B\),則可以互相傳遞信息,進而構成了一對組合。由於所有的觀察哨在一個環上, 可轉化為在環形鏈表中,求一個數\(a\)左右兩側離它最近且大於它的數\(b\)和\(c\),進而\(a\)與\(b\),\(a\)與\(c\)便是滿足要求的組合。
考慮特殊情況,環形鏈表中的最大值和次大值,他們無法找到與之配對的兩個數,只能與彼此組成一對組合。注意,以上情況假設無相同大小的數值出現,在這種情況下通解為:\((n - 2) · 2 + 1\)。
對於有重復數字的情況,\(A(N_1), B(N_2), C(N_3), A > B \space{} and\space{} C > B\),\(B\)自身能夠構成的組合數目有\(C_{N_2}^2\),即\(N_2·(N_2-1)/2\);\(B\)與\(A\),\(B\)與\(C\) 均能構成\(N_2\) 種組合,最終結果為 \(N_2·(N_2-1)/2 + N_2 + N_2\) 。
為了求出一個數左右都大於它的數,采用單調棧來實現,棧底到棧頂一次遞減。在具體算法實現中,
-
遍歷一次數組,找出相鄰的相同元素,記錄其重復次數,消除重復性(不相鄰元素依然存在重復可能),同時記錄數組最大值出現的位置\(max_i\);
-
創建堆棧(也可以是vector),從最大元素的位置開始遍歷數組,執行如下操作:
- 堆棧為空, 將數組元素直接壓入堆棧;
- 堆棧非空, 將要壓入的元素
v[i]
與棧頂元素stack[top]
比較, 如果大於棧頂元素,加上棧頂元素的組合數目,然后彈出,重復此過程直至遇到大於v[i]
的元素,壓入v[i]
; - 如果等於棧頂元素,將重復數目相加,繼續執行;
- 如果小於棧頂元素,直接壓入;
-
當數組遍歷完之后,考慮堆棧的狀態。如果不為空,每彈出一個元素,加上其組合數目,當堆棧只剩一個元素時(**最大值),有兩種情況:
-
剩余元素的重復次數\(n\)大於1,此時次大元素能夠組成的組合為 \(N_2·(N_2-1)/2 + N_2 + N_2\) ;
-
剩余元素重復次數為1,此時次大元素能夠組成的組合為 $N_2·(N_2-1)/2 + N_2 ;
舉個例子, AAAB連成一個環,B與其左側的A可組成一個組合,與第一個A也能形成一個組合;但如果是AB的情況,則B只能與左側的A形成組合,也就是說,從B看向A與從A看向B是不同的視角。
-
代碼
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node{
int val;
long count;
node(int v, int c = 1): val(v), count(c){}
};
int main() {
ios::sync_with_stdio(false);
int N;
while(cin >> N){
vector<int> watch(N);
for(int i = 0; i < N; ++i)
cin >> watch[i];
vector<node> v;
node tmp(watch[0]);
int max_h = watch[0];
int max_index = 0;
for(int i = 1; i < N; ++i){
if(watch[i] == watch[i - 1])
tmp.count ++;
else{
v.push_back(tmp);
if( max_h < tmp.val){
max_h = tmp.val;
max_index = v.size() - 1;
}
tmp.val = watch[i];
tmp.count = 1;
}
}
// 最后一個元素
v.push_back(tmp);
if( max_h < tmp.val){
max_h = tmp.val;
max_index = v.size() - 1;
}
int n = 0;
long count = 0;
vector<node> stack;
for(int i = max_index; n < v.size(); ++n, i = (i+1)%v.size()){
while( stack.size() && v[i].val > stack[stack.size() - 1].val){
node & tmp = stack[stack.size() - 1];
count += tmp.count + tmp.count*(tmp.count - 1)/2;
stack.pop_back();
if(stack.size()) count += tmp.count;
}
if( stack.size()){
if(v[i].val == stack[stack.size() - 1].val)
stack[stack.size() - 1].count += v[i].count;
else
stack.push_back(v[i]);
} else
stack.push_back(v[i]);
}
while(stack.size()){
node & tmp = stack[stack.size() - 1];
count += tmp.count*(tmp.count - 1)/2;
stack.pop_back();
if(stack.size()) count += 2 * tmp.count;
if(stack.size()==1 &&stack[stack.size()-1].count==1) count-=tmp.count;
}
cout << count<<endl;
}
return 0;
}