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++解題報告: