動態規劃:P5569 [SDOI2008] 石子合併 GarsiaWachs演算法
阿新 • • 發佈:2022-04-17
P5569 [SDOI2008] 石子合併
這題就是P1775石子合併的資料加強版,我們那題採用的是區間DP,時間複雜度為O(n3) (4*1e4)的三次方=1.6*1e13,顯然超時。這裡就必須用一個演算法,叫做GarsiaWachs演算法,可以降低時間複雜度至n2甚至nlogn.
GarsiaWachs演算法:他的做法就是對於一個序列a,先int ans=0,然後從左到右找到a[k-1]<a[k+1],將a[k]和a[k-1]合併成一個新的元素a[temp],從a[k-1]向左尋找,找到第一個比他大的,插入在他後面,ans+=a[temp],注意要把a[-1]和a[終點]定義為無窮大,方便尋找插入。然後再重複這個操作,直到序列中沒有a[k-1]<a[k+1].原理我也不會推導,只知道這麼個結論。
這是我的程式碼:利用stl來操作,但是要開O2優化,否則只有70分,T3個點。
1 //GarsiaWachs 2 //開O2 3 #include<iostream> 4 #include<cmath> 5 #include<algorithm> 6 #include<vector> 7 #include<climits>//INT_MAX的標頭檔案 8 using namespace std; 9 int read() 10 { 11 int f = 1; 12 int ans = 0; 13 charc = getchar(); 14 if (c > '9' || c < '0') 15 { 16 f = -1; 17 c = getchar(); 18 } 19 while (c >= '0' && c <= '9') 20 { 21 ans = (ans << 1) + (ans << 3) + (c - '0'); 22 c = getchar(); 23 } 24 return f * ans; 25 }26 int main() 27 { 28 int n; 29 n = read(); 30 vector<int>v; 31 v.push_back(INT_MAX - 1); 32 for (int i = 1; i <= n; ++i) 33 { 34 v.push_back(read()); 35 } 36 v.push_back(INT_MAX); 37 int k, j, temp, sum = 0; 38 while (n-- > 1) 39 { 40 for (k = 1; k <= n; ++k) 41 { 42 if (v[k - 1] < v[k + 1]) 43 break; 44 } 45 temp = v[k - 1] + v[k]; 46 for (j = k - 1; j >= 0; --j) 47 { 48 if (v[j] > temp) 49 break; 50 } 51 v.erase(v.begin() + k - 1);//刪掉k-1; 52 v.erase(v.begin() + k - 1);//刪掉k 53 v.insert(v.begin() + j + 1, temp);//插入和 54 sum += temp; 55 } 56 cout << sum; 57 return 0; 58 59 }
沒開O2:
開O2:
最後補充一下:這個GarsiaWachs演算法的標準程式碼模板,時間複雜度也特別低,高效通過,但是我看了很久也理解不了他的原理。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn=50005; 4 5 int n; 6 int a[maxn]; 7 int num; 8 int ans; 9 10 void combine(int k){ 11 12 int temp=a[k]+a[k-1]; 13 ans+=temp; 14 for (int i=k;i<num-1;i++){ 15 a[i]=a[i+1]; 16 } 17 num--; 18 19 int j=0; 20 for (j=k-1;j>0&&a[j-1]<temp;j--){ 21 a[j]=a[j-1]; 22 } 23 a[j]=temp; 24 25 while (j>=2&&a[j]>=a[j-2]){ 26 int d=num-j; 27 combine(j-1); 28 j=num-d; 29 } 30 } 31 32 int main() 33 { 34 cin >> n; 35 for (int i=0;i<n;i++){ 36 cin >> a[i]; 37 } 38 num=1; 39 ans=0; 40 for (int i=1;i<n;i++){ 41 a[num++]=a[i]; 42 while (num>=3&&a[num-3]<=a[num-1]){ 43 combine(num-2); 44 } 45 } 46 while (num>1){ 47 combine(num-1); 48 } 49 50 cout << ans << endl; 51 }