1. 程式人生 > 其它 >動態規劃:P5569 [SDOI2008] 石子合併 GarsiaWachs演算法

動態規劃:P5569 [SDOI2008] 石子合併 GarsiaWachs演算法

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     char
c = 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 }