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 }
第三种(重点来了): 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 }
(哦对了,注意开 long long,我一开始没开,后面爆了,数据乘起来蛮大的)
加油加油加油!!!fighting fighting fighting!!!