[题目描述]
给定一个长度为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
转载请注明出处