石子合并问题

来源:互联网 发布:logo软件 编辑:程序博客网 时间:2024/06/02 00:08

问题:

在一个操场上摆放着一排N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将N堆石子合并成一堆的最小得分。

O(n3)

f[i][j]=min{f[i][k]+f[k+1][j]}(ikj)+w(i,j)

这应该是普及组的DP。

(对于环形只需要复制一遍来个2n的DP就好了)
环形DP(Luogu1880):http://paste.ubuntu.com/25619945/

O(n2)

首先有四边形不等式。
四边形不等式需要满足的条件:
1.f[i][j]f[i][j](iijj)
即区间包含的单调性。

2.f[i][j]=min{f[i][k1]+f[k,j]}+w(i,j)(ikj)
w(i,j)+w(i,j)w(i,j)+w(i,j)(ii<jj)

如果满足四边形不等式,那么f满足决策单调。

显然合并石子的dp函数满足以上任意两个条件。所以决策单调性优化,复杂度变成了O(n2)
(只能做最小值)
Code:http://paste.ubuntu.com/25620177/

当然,利用贪心思想,可以得出最大值的O(n2)转移:

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);}