1. 程式人生 > >PAT-BASIC1030——完美數列/PAT-ADVANCED1085——Perfect Sequence

PAT-BASIC1030——完美數列/PAT-ADVANCED1085——Perfect Sequence

原題連結:

題目描述:

PAT-BASIC1030:

PAT-ADVANCED1085:

知識點:滑動視窗

思路一:遞迴求解(在PAT中提交測試點4會記憶體超限)

首先對陣列從小到大進行排序。

遞迴函式的定義:int perfectArrays(vector<long> nums, int left, int right, int p);

在nums的索引[left, right]之間尋找完美數列的最大個數。

遞迴終止條件

當left == right時,直接返回1。

遞迴過程

(1)如果[left, right]之間的序列本身是一個完美序列,直接返回right - left + 1。

(2)如果[left, right]之間的序列本身不是一個完美序列,返回[left + 1, right]與[left, right - 1]之中完美序列的最大個數。

注意點:

需要用long型變數儲存陣列中的元素,因為題目聲明瞭每個數不超過10 ^ 9。int型會因為越界而無法通過測試點5。

時間複雜度是O(n ^ 2),其中n為輸入陣列的長度。空間複雜度即遞迴深度,是O(n)。

C++程式碼:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int perfectArrays(vector<long> nums, int left, int right, int p);

int main(){
	int n, p;
	
	cin >> n >> p;
	
	vector<long> nums;
	long tempNum;
	for(int i = 0; i < n; i++){
		cin >> tempNum;
		nums.push_back(tempNum);
	}
	
	sort(nums.begin(), nums.end());
	
	cout << perfectArrays(nums, 0, n - 1, p);
	
	return 0;
}

int perfectArrays(vector<long> nums, int left, int right, int p){
	if(left == right){
		return 1;
	}
	if(nums[right] <= nums[left] * p){
		return right - left + 1;
	}else{
		int result1 = perfectArrays(nums, left + 1, right, p);
		int result2 = perfectArrays(nums, left, right - 1, p);
		if(result1 >= result2){
			return result1;
		}
		return result2;
	}
}

C++解題報告:

思路二:動態規劃(在PAT中提交測試點4會記憶體超限)

首先對陣列從小到大進行排序。

狀態定義

f(x, y) -------- 陣列中索引在[x, y]之間的元素所對應的完美數列的元素個數

狀態轉移

(1)如果[x, y]之間的序列本身是一個完美序列,f(x, y) = y - x + 1。

(2)如果[x, y]之間的序列本身不是一個完美序列,f(x, y) = max(f(x + 1, y), f(x, y - 1))。

時間複雜度是O(n ^ 2),其中n為輸入陣列的長度。空間複雜度是O(n ^ 2)。

注意點:

需要用long型變數儲存陣列中的元素,因為題目聲明瞭每個數不超過10 ^ 9。int型會因為越界而無法通過測試點5。

C++程式碼:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int main() {
	int n;
	long p;
	cin >> n >> p;

	long temp;
	vector<long> nums;
	for (int i = 0; i < n; i++) {
		cin >> temp;
		nums.push_back(temp);
	}
	sort(nums.begin(), nums.end());

	int **dp = new int*[n];
	for (int i = 0; i < n; i++) {
		dp[i] = new int[n];
	}

	for (int i = 0; i < n; i++) {
		dp[i][i] = 1;
	}

	for (int k = -1; k >= 1 - n; k--) {
		for (int i = 0; i <= n + k - 1; i++) {
			if (nums[i - k] <= nums[i] * p) {
				dp[i][i - k] = 1 - k;
			} else {
				dp[i][i - k] = dp[i + 1][i - k];
				if (dp[i][i - k - 1] > dp[i][i - k]) {
					dp[i][i - k] = dp[i][i - k - 1];
				}
			}
		}
		delete dp[n + k];
	}

	int result;

	result = dp[0][n - 1];
	delete dp;

	cout << result;
}

C++解題報告:

思路三:滑動視窗

首先對陣列從小到大進行排序。

對每一個左邊界i,尋找其右邊界j,使得能夠滿足nums[j] <= nums[i] * p。每次尋找右邊界j都是從i + result索引開始找起,其中result為當前的完美數列的最多個數。

(1)如果滿足條件nums[j] <= nums[i] * p,則更新result的值。

(2)一旦j不滿足nums[j] <= nums[i] * p就可以break跳出內層迴圈,尋找下一個左邊界對應的完美序列的最多個數。因為陣列已經經過排序,j繼續增大更加不會滿足條件nums[j] <= nums[i] * p。

時間複雜度是O(n),其中n為輸入陣列的長度。空間複雜度是O(1)。

注意點:

需要用long型變數儲存陣列中的元素,因為題目聲明瞭每個數不超過10 ^ 9。int型會因為越界而無法通過測試點5。

C++程式碼:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int main() {
	int n;
	long p;
	cin >> n >> p;

	long temp;
	vector<long> nums;
	for (int i = 0; i < n; i++) {
		cin >> temp;
		nums.push_back(temp);
	}
	sort(nums.begin(), nums.end());

	int result = 0;
	for (int i = 0; i < nums.size(); i++) {
		for (int j = i + result; j < nums.size(); j++) {
			if (nums[j] <= nums[i] * p) {
				if (j - i + 1 > result) {
					result = j - i + 1;
				}
			} else {
				break;
			}
		}
	}

	cout << result;
}

C++解題報告: