[题解]和

来源:互联网 发布:怎么发淘宝链接给别人 编辑:程序博客网 时间:2024/04/30 11:06

[题目描述]

给定一个长度为n的序列,你每次可以合并相邻两个元素,新的元素为这两个元素的和。你需要使得若干
次合并之后的序列非降,求最小合并次数。

[数据范围]

n<=1500

[题解]

      这道题的标程用的是n^2log(n)的dp,所以才会有奇葩的1500的范围.

      然而,这道题完全可以在O(n^2)的时间内解决.

      考虑前i个数的决策.如果我们要合并成合法序列后,最后的值最小,那么我们一定要使合并的次数最小.因为在如果合并次数最少且最后的值最小的情况下再进行合并,最后的值显然不会变大,即:不可能出现一种情况,使得合并的次数不是最少而最后面的值最小.因为我们一切的合并操作都是"被逼"的,所以多合并不可能使最后的数变大,于是上面的结论也可以很容易的想出来.

Code:

program sequence;type int=longint;var        i,j,k,m,n:int;        a,f,g,s:array[0..1500]of int;begin        assign(input,'sequence.in');reset(input);        assign(output,'sequence.out');rewrite(output);        read(n);        for i:=1 to n do begin read(a[i]);s[i]:=s[i-1]+a[i];end;        fillchar(f,sizeof(f),100);        fillchar(g,sizeof(g),100);        f[1]:=0;g[1]:=a[1];        for i:=2 to n do begin                for j:=1 to i-1 do begin                        if(f[j]+i-j-1<=f[i])and(g[j]<=s[i]-s[j])then begin                                g[i]:=s[i]-s[j];f[i]:=i-j-1+f[j];                        end;                end;        end;        write(f[n]);        close(input);close(output);end.

考试时因为前两题太水了,本以为第三题会搞一道难题防AK的,所以就随便打了个贪心,只搞到了10分.知道正解这么简单后简直想吐血= =.

有很多人觉得这样dp的正确性不好证,其实这样做的正确性是显然的,到时候我再将完整版题解搞上来.

//=================================================================================================

好了,考也考完了.现在我来讲一下这道题目的线性做法.


将方程变形得到:

F[i]=min(f[j]-j)+i-1,G[j]+s[j]<=s[i].

显然,F[j]-j随j单调不增,s[i]单调上升.

于是,我们维护这样一个单调队列,使得下标单调递增(即F[j]-j单调递减),G[j]+s[j]单调上升,之后怎么做应该都会了吧.


BY QW

转载请注明出处