烽火臺問題(單調棧應用)

一、題目描述 

問題描述:給你一個數組,表示烽火臺的信號值,烽火臺是首尾相接的環形組成的,返回滿足下列條件有多少對烽火臺能兩兩通訊:

1)相鄰的烽火臺能相互通訊

2)不相鄰的烽火臺能相互通訊的條件爲:兩個烽火臺經過的所有烽火臺的信號值都不大於這兩個中的最小那個。

比如數組【1 2 4 3 5】相鄰的【1 2】【2 3】【3 4】【4 5】【5 1】,不相鄰的【5 4】可以通訊,
因爲中間的3比4和5中小的要小,還有【5 2】也可以通訊,但是【3 2】【4 1】就不能相互通訊了。
所以一共返回7對

二、分析 

思路:使用單調棧實現,

1)遍歷數組找到最大的元素壓入棧底,後面入棧保證棧底到棧頂從大到小,棧中記錄元素值和當前值出現的次數

2)如果遇到入棧元素比棧頂元素大,出棧結算,下面詳細介紹結算的過程

3)如果遇到相同的元素則繼續入棧,棧中元素出現的次數加1即可,比如下面這個例子

 

 4)現在看一種普遍的情況,總結出出棧結算的方式。

  5)如果沒有出棧的情況或者遍歷完了元素也沒有出棧的情況

6)現在考慮只有最後兩條記錄的時候,有兩種情況,分別是棧底元素出現次數≥2和小於2的情況,看圖

總結:還沒遍歷完數組壓棧和彈棧過程中,C(times,2)+times*2.
遍歷完數組之後,彈棧時,相同元素內部還是有C(times,2)對,
如果棧底元素times大於等於2結算方式同上,爲1則爲C(times,2) + times*1,
因爲times爲1的時候順時針和逆時針都和該元素成對,算一次即可

三、代碼實現

#include<iostream>
#include<stack>
using namespace std;

class Data {
public:
	int val;
	int times;
	Data(int num) :val(num), times(1) {
	}
};
long getInerNum(int n) {
	//如果n爲1則返回0個,否則就是C(times,2),C(k,2)=k!/((k-2)!*2!) = k(k-1)/2
	return n == 1L ? 0L : (long)n*(long)(n - 1) / 2L;
}
long communication(int arr[], int arr_len) {
	if (arr == NULL || arr_len < 2) {//數組沒有元素或者只有一個元素,返回0對
		return 0;
	}
	int max_index = 0;
	for (int i = 0; i < arr_len; ++i) {//在數組中找最大值的位置
		max_index = arr[max_index] > arr[i] ? max_index : i;
	}
	int value = arr[max_index];

	//愛的魔力轉圈圈,如果到達最後一個則下一個從0開始,否則爲max_index+1
	int next_index = max_index + 1 > arr_len - 1 ? 0 : max_index + 1;//從最大值位置的下一個開始遍歷
	long res = 0L;

	stack<Data*> sta;
	sta.push(new Data(value));

	while (next_index!=max_index) {//如果next_index回到max_index表示我們遍歷結束
		value = arr[next_index];
		while (value > sta.top()->val) {//這裏採用最大值打底,所以棧不可能彈成空
			int times = sta.top()->times;
			res += (getInerNum(times) + times * 2);
			sta.pop();
		}
		//下面表示無法彈了,無法彈的原因有兩個,小於或等於棧頂元素
		if (!sta.empty() && sta.top()->val == value)
			sta.top()->times++;
		else
			sta.push(new Data(value));

		next_index = next_index+1 > arr_len - 1 ? 0: next_index + 1;//更新next_index
	}
	while (!sta.empty()) {
		int times = sta.top()->times;
		sta.pop();
		res += getInerNum(times);//如果不是倒數第二和倒數第一,則固定就是C(times,2)和times*2
		if (!sta.empty()) {
			res += times;
			if (sta.size() > 1) {//pop了之後還大於1表示棧中有3個以上元素,再加一次times
				res += times;
			}
			else//否則只有2個元素,只有兩個元素的時候考慮棧底元素的times是否大於,是則再加一次,否則不加
				res += sta.top()->times > 1 ? times : 0;
		}
		
	}
	return res;
}
int main() {
	int n;
	while (cin >> n) {
		int* arr = new int[n];
		for (int i = 0; i < n; ++i) {
			cin >> arr[i];
		}
		cout << communication(arr,n) << endl;
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章