石子合并问题
来源:互联网 发布:logo软件 编辑:程序博客网 时间:2024/06/02 00:08
问题:
在一个操场上摆放着一排N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将N堆石子合并成一堆的最小得分。
O(n3)
这应该是普及组的DP。
(对于环形只需要复制一遍来个
环形DP(Luogu1880):http://paste.ubuntu.com/25619945/
O(n2)
首先有四边形不等式。
四边形不等式需要满足的条件:
1.
即区间包含的单调性。
2.
如果满足四边形不等式,那么
显然合并石子的
(只能做最小值)
Code:http://paste.ubuntu.com/25620177/
当然,利用贪心思想,可以得出最大值的
for (int i=n*2-1;i>=1;--i) for (int j=i+1;j<=min(n*2-1,i+n-1);++j) f[i][j]=max(f[i][i]+f[i+1][j],f[j][j]+f[i][j-1])+w[j]-w[i-1];
O(nlogn)
经过上面的贪心,应该可以发现:
如果先合并一个值两边的较大值,结果不会最优。(这不是严格证明,严格证明请看:《The Art of Computer Programming》第3卷6.2.2节Algorithm G和Lemma W,Lemma X,Lemma Y,Lemma Z)。
GarsiaWachs算法
http://www.cnblogs.com/lwq12138/p/5425465.html
个人认为还是比较简单的(在知道了GarsiaWachs算法后)
我只知道结论,设一个序列是A[0..n-1],每次寻找最小的一个满足A[k-1]<=A[k+1]的k,(方便起见设A[-1]和A[n]等于正无穷大)
那么我们就把A[k]与A[k-1]合并,之后找最大的一个满足A[j]>A[k]+A[k-1]的j,把合并后的值A[k]+A[k-1]插入A[j]的后面。
有定理保证,如此操作后问题的答案不会改变。
举个例子:
186 64 35 32 103
因为35<103,所以最小的k是3,我们先把35和32删除,得到他们的和67,并向前寻找一个第一个超过67的数,把67插入到他后面
186 64(k=3,A[3]与A[2]都被删除了) 103
186 67(遇到了从右向左第一个比67大的数,我们把67插入到他后面) 64 103
186 67 64 103 (有定理保证这个序列的答案加上67就等于原序列的答案)
现在由5个数变为4个数了,继续!
186 (k=2,67和64被删除了)103
186 131(就插入在这里) 103
186 131 103
现在k=2(别忘了,设A[-1]和A[n]等于正无穷大)
234 186
420
最后的答案呢?就是各次合并的重量之和呗。420+234+131+67=852;
BZOJ3229:
#include<bits/stdc++.h>using namespace std;struct IO{ streambuf *ib,*ob; inline void init(){ ios::sync_with_stdio(false); cin.tie(NULL);cout.tie(NULL); ib=cin.rdbuf();ob=cout.rdbuf(); } inline int read(){ static char ch;static int i,f; ch=ib->sbumpc();i=0,f=1; while(!isdigit(ch)){ if(ch==-1)return false; if(ch=='-')f=-1; ch=ib->sbumpc(); } while(isdigit(ch)){ i=(i+(i<<2)<<1)+ch-'0'; ch=ib->sbumpc(); } return ((f>0)?i:-i); } inline void W(long long x){ static int buf[50]; if(!x){ob->sputc('0');return;} if(x<0){ob->sputc('-');x=-x;} while(x){buf[++buf[0]]=x%10;x/=10;} while(buf[0]){ob->sputc(buf[buf[0]--]+'0');} }}io;const int Maxn=5e4+5;int n,m,a[Maxn];const int INF=0x3f3f3f3f;long long ans;int main(){ io.init();n=io.read();m=n+1; a[0]=a[n+1]=INF; for(int i=1;i<=n;i++)a[i]=io.read(); for(int i=1;i<n;i++) { int pos=0; for(int j=1;j<m;j++)if(a[j+1]>=a[j-1]){pos=j;break;} a[pos-1]+=a[pos];ans+=a[pos-1]; for(int j=pos;j<m;j++)a[j]=a[j+1]; m--; for(int j=pos-1;j>=1;j--) { if(a[j-1]>a[j])break; swap(a[j-1],a[j]); } } io.W(ans);}
- 石子合并问题
- 石子合并问题
- 【dp】石子合并问题
- 石子合并问题
- 石子合并问题
- 经典石子合并问题
- NK1137 石子合并问题
- 经典石子合并问题
- 石子合并问题
- 石子合并问题
- 【FJOI2014】石子合并问题
- DP_石子合并问题
- 石子合并问题
- 石子合并问题
- 石子合并问题
- 石子合并问题
- 石子合并问题
- 石子合并问题
- 1069. 微博转发抽奖(20)
- servelt与session实现登录并验证的源码
- JAVA文件工具类之——文件写入(byte数组、String方式、url写入方式)
- IAR 编译完毕时自动删除 .obj 中间文件的方法
- FPGA设计之常用Verilog语法
- 石子合并问题
- 【转】C#获取当前日期时间(转)
- Java面向对象基础
- storyboard适应屏幕
- Html中框架的使用
- 面试问题a++是线程安全的么
- spring boot多数据源
- webpack的安装步骤
- JavaScript的值和引用