石子合并问题
2017-03-31 20:29
211 查看
石子合并(一)
时间限制:1000 ms | 内存限制:65535 KB难度:3 描述 有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。 输入有多组测试数据,输入到文件结束。每组测试数据第一行有一个整数n,表示有n堆石子。接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开输出输出总代价的最小值,占单独的一行样例输入3 1 2 3 7 13 7 8 16 21 4 18样例输出
9239最普通的算法O(n^3):#include <fstream>#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>using namespace std;const int N=205;const int INF=0x7fffffff;int n;int a,sum,dp;void f();int main(){//freopen("D:\\input.in","r",stdin);while(~scanf("%d",&n)){sum[0]=0;for(int i=1;i<=n;i++){scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}f();printf("%d\n",dp[1]);}return 0;}void f(){for(int i=1;i<=n;i++) dp[i][i]=0;for(int r=1;r<n;r++){for(int i=1;i<n;i++){int j=i+r;if(j>n) break;dp[i][j]=INF;for(int k=i;k<=j;k++){dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);}dp[i][j]+=sum[j]-sum[i-1];}}}224ms其中,dp[i][j]代表i到j堆的最优值,sum[i]代表第1堆到第i堆的数目总和。有:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j])+sum[j]-sum[i-1]。 考虑四边形不等式优化:接近O(n^2):#include <fstream>#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>using namespace std;const int N=205;const int INF=0x7fffffff;int n;int a,sum,dp,s;void f();int main(){//freopen("D:\\input.in","r",stdin);while(~scanf("%d",&n)){sum[0]=0;for(int i=1;i<=n;i++){scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}f();printf("%d\n",dp[1]);}return 0;}void f(){for(int i=1;i<=n;i++) dp[i][i]=0,s[i][i]=i;for(int r=1;r<n;r++){for(int i=1;i<n;i++){int j=i+r;if(j>n) break;dp[i][j]=INF;for(int k=s[i][j-1];k<=s[i+1][j];k++){if(dp[i][j]>dp[i][k]+dp[k+1][j]){dp[i][j]=dp[i][k]+dp[k+1][j];s[i][j]=k;}}dp[i][j]+=sum[j]-sum[i-1];}}}32ms优化:原状态转移方程中的k的枚举范围便可以从原来的(i~j-1)变为(s[i,j-1]~s[i+1,j])。解释下:四边形不等式优化动态规划原理:1.当决策代价函数w[i][j]满足w[i][j]+w[i’][j’]<=w[I;][j]+w[i][j’](i<=i’<=j<=j’)时,称w满足四边形不等式.当函数w[i][j]满足w[i’][j]<=w[i][j’]i<=i’<=j<=j’)时,称w关于区间包含关系单调.2.如果状态转移方程m为
![](http://p.blog.csdn.net/images/p_blog_csdn_net/find_my_dream/EntryImages/20091203/1.png)
![](http://p.blog.csdn.net/images/p_blog_csdn_net/find_my_dream/EntryImages/20091203/2.png)
#include <iostream>#include <cstring>#include <cstdio>using namespace std;const int N = 205;int stone;int n,t,ans;void combine(int k);int main(){while(cin>>n){for(int i=0;i<n;i++)scanf("%d",stone+i);t = 1;ans = 0;for(int i=1;i<n;i++){stone[t++] = stone[i];while(t >= 3 && stone[t-3] <= stone[t-1])combine(t-2);}while(t > 1)combine(t-1);printf("%d\n",ans);}return 0;}void combine(int k){int tmp = stone[k] + stone[k-1];ans += tmp;for(int i=k;i<t-1;i++)stone[i] = stone[i+1];t--;int j = 0;for(j=k-1;j>0 && stone[j-1] < tmp;j--)stone[j] = stone[j-1];stone[j] = tmp;while(j >= 2 && stone[j] >= stone[j-2]){int d = t - j;combine(j-1);j = t - d;}}4ms解释:对于石子合并问题,有一个最好的算法,那就是GarsiaWachs算法。时间复杂度为O(n^2)。它的步骤如下:设序列是stone[],从左往右,找一个满足stone[k-1] <= stone[k+1]的k,找到后合并stone[k]和stone[k-1],再从当前位置开始向左找最大的j,使其满足stone[j] > stone[k]+stone[k-1],插到j的后面就行。一直重复,直到只剩下一堆石子就可以了。在这个过程中,可以假设stone[-1]和stone是正无穷的。举个例子:186 64 35 32 103因为35<103,所以最小的k是3,我们先把35和32删除,得到他们的和67,并向前寻找一个第一个超过67的数,把67插入到他后面,得到:186 67 64 103,现在由5个数变为4个数了,继续:186 131 103,现在k=2(别忘了,设A[-1]和A等于正无穷大)234 186,最后得到420。最后的答案呢?就是各次合并的重量之和,即420+234+131+67=852。 基本思想是通过树的最优性得到一个节点间深度的约束,之后证明操作一次之后的解可以和原来的解一一对应,并保证节点移动之后他所在的深度不会改变。具体实现这个算法需要一点技巧,精髓在于不停快速寻找最小的k,即维护一个“2-递减序列”朴素的实现的时间复杂度是O(n*n),但可以用一个平衡树来优化,使得最终复杂度为O(nlogn)。 GarsiaWachs算法优化+小细节优化:
#include <fstream>#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>using namespace std;const int N = 205;const int INF = 0x7fffffff;int stone;int n,t,ans;void combine(int k){int tmp = stone[k] + stone[k-1];ans += tmp;for(int i=k;i<t-1;i++)stone[i] = stone[i+1];t--;int j = 0;for(j=k-1;stone[j-1] < tmp;j--)stone[j] = stone[j-1];stone[j] = tmp;while(j >= 2 && stone[j] >= stone[j-2]){int d = t - j;combine(j-1);j = t - d;}}int main(){//freopen("D:\\input.in","r",stdin);while(~scanf("%d",&n)){for(int i=1;i<=n;i++)scanf("%d",stone+i);stone[0]=INF;stone[n+1]=INF-1;t = 3;ans = 0;for(int i=3;i<=n+1;i++){stone[t++] = stone[i];while(stone[t-3] <= stone[t-1])combine(t-2);}while(t > 3) combine(t-1);printf("%d\n",ans);}return 0;}0ms
相关文章推荐
- 石子合并问题
- 动态规划解循环石子堆合并问题
- poj 1738 An old Stone Game(区间dp 合并石子问题直线型)
- 动态规划:石子合并问题
- 石子合并问题(动态规划)
- 石子合并问题
- NK 1137: 石子合并问题
- scau 9209 石子合并问题
- 动态规划思想:石子合并问题
- 石子合并问题 --动态规划--解法1
- 石子合并问题 -- 任意版
- 动态规划解石子合并问题
- 合并石子问题 贪心+最大最小堆基本操作
- 石子合并问题
- 环形石子合并问题 - 经典DP问题
- 石子合并;圈型;动态规划;重点在于处理圈型问题;代码内有算法解释;
- 【dp】石子合并问题
- 石子合并问题
- NK1137 石子合并问题
- NKOJ 1137 石子合并问题 (区间dp)