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

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!!!

猜你喜欢

转载自www.cnblogs.com/Frank-King/p/9313346.html