1. 程式人生 > >51Nod 1021~1023 石子合並 (逐步加強版) 【dp】

51Nod 1021~1023 石子合並 (逐步加強版) 【dp】

四邊形 git pac min == hide char none get

51Nod 1021~1023 石子合並

小結:

這裏給出三種做法。

第一種:n^3 最基礎的合並類dp, f[i,j]=min(f[i,j],f[i,k]+f[k,j]+w[i,j])

這種代碼不貼了,網上多的是。鄙人太弱(逃~)

第二種: n^2 四邊形不等式優化。。。

主要是優化了 k 的枚舉, k 從 s[i][j-1] 枚舉到 s[i+1][j] 就行了。。。 ???證明(不會誒,以後再說)

貼代碼:

技術分享圖片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=2005;
 4 int
n,f[N][N]={0},a[N][N]={0}; 5 int s[N][N]; 6 inline int read() 7 { 8 int x=0,f=1; char ch=getchar(); 9 while (!isdigit(ch)) f=(ch==-)?-f:f,ch=getchar(); 10 while (isdigit(ch)) x=(x<<3)+(x<<1)+ch-0,ch=getchar(); 11 return x*f; 12 } 13 int main() 14 { 15 memset(f,1,sizeof
(f)); 16 scanf("%d",&n); 17 for (int i=0; i<n; i++) 18 { 19 scanf("%d",&a[i][i]); 20 a[n+i][n+i]=a[i][i]; 21 } 22 for (int i=0; i<n*2; i++) 23 { 24 s[i][i]=i; 25 f[i][i]=0; 26 } 27 for (int i=0; i<n*2-1; i++) 28 for (int
j=i+1; j<n*2-1; j++) 29 a[i][j]=a[i][j-1]+a[j][j]; 30 for (int l=1; l<n; l++) 31 { 32 for (int i=0; i+l<n*2-1; i++) 33 { 34 int j=i+l; 35 for (int k=s[i][j-1]; k<=s[i+1][j]; k++) 36 { 37 if (f[i][j]>a[i][j]+f[i][k]+f[k+1][j]) 38 { 39 f[i][j]=a[i][j]+f[i][k]+f[k+1][j]; 40 s[i][j]=k; 41 } 42 } 43 } 44 } 45 int ans=f[0][n-1]; 46 for (int i=1; i<n; i++) 47 if (ans>f[i][i+n-1]) 48 ans=f[i][i+n-1]; 49 printf("%d\n",ans); 50 return 0; 51 }
View Code

第三種(重點來了): GarsiaWachs算法 (是不是很上檔次啊)

大約 n log n (不要問我為什麽,木雞啊)

對於剩下的 k 堆石子,找出最小的堆 i, 並且滿足 f[i-2]<=f[i],那麽就優先合並 f[i-2] , f[i-1] 這兩堆。

然後把合並起來的一堆,從這往前找,找到第一個比它大的堆,插在它後面。以此循環,直到最後剩一堆。

證明未知。。玄學做法。。。

貼代碼:

技術分享圖片
 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 using namespace std;
 4 const int N=50005;
 5 int stone[N],n,t;
 6 LL ans;
 7 void combine(int k)
 8 {
 9     int tmp=stone[k]+stone[k-1];
10     ans+=(LL)tmp;
11     for (int i=k; i<t-1; i++)
12       stone[i]=stone[i+1];
13     t--;
14     int j;
15     for (j=k-1; j>0 && stone[j-1] < tmp; j--)
16       stone[j]=stone[j-1];
17     stone[j]=tmp;
18     while (j>=2 && stone[j]>=stone[j-2])
19     {
20         int d=t-j;
21         combine(j-1);
22         j=t-d;
23     }
24 }
25 int main()
26 {
27     scanf("%d",&n);
28     for (int i=0; i<n; i++)
29       scanf("%d",&stone[i]);
30     t=1; ans=0;
31     for (int i=1; i<n; i++)
32     {
33         stone[t++]=stone[i];
34         while (t>=3 && stone[t-3] <=stone[t-1])
35           combine(t-2);
36     }
37     while (t>1) combine(t-1);
38     printf("%lld\n",ans);
39     return 0;
40 }
View Code

(哦對了,註意開 long long,我一開始沒開,後面爆了,數據乘起來蠻大的)

加油加油加油!!!fighting fighting fighting!!!

51Nod 1021~1023 石子合並 (逐步加強版) 【dp】