题目大意:把n个数分为连续的k段,使得每一段的最大值最小(如果多解,则前面段的数越少越好,即后面段的数越多越好)
样例输入:
9 3
1 2 3 4 5 6 7 8 9
样例输出:
1 5
6 7
8 9
输出表示把n个数如何分段,每一段的开头st和结尾en。
这道题目可以用dp的方法,设f[i,j]表示前i个数分为j段的最优值。
f[i,j]的状态转移方程显然是非常简单的,注意初始值设定为f[1,j]:=d[j](1<=j<=m,d[j]=d[j-1]+a[j])。然后这道题还需注意的就是输出。
输出需要用贪心的算法。首先因为题目说了如果有多解,要求后面的段多数字,那么因为我们输出都是从后往前逆推的,所以我们直接就判断如果后面的段能继续装下数字且不超过最优值就一直装,当不能装的时候就再判断下一段,以此类推,可以用递归输出,更加的好处理。
代码:
var
i,j,m,n,k:Longint;
a,b:Array[0..500] of Longint;
f:array[0..500,0..500] of Longint;
function max(x,y:Longint):Longint;
begin
if x>Y then exit(x); exit(y);
end;
function min(x,y:Longint):Longint;
begin
if x<Y then exit(x); exit(y);
end;
procedure print(i,j:Longint);
var
t,x:Longint;
begin
if j=0 then exit;
if j=1 then
begin
writeln(1,' ',i);
exit;
end;
t:=i;
x:=a[i];
while x+a[t-1]<=f[n,m] do
begin
x:=x+a[t-1];
t:=t-1;
end;
print(t-1,j-1);
writeln(t,' ',i);
end;
begin
readln(m,n);
fillchar(f,sizeof(f),$7f);
for i:=1 to m do
begin
read(a[i]);
b[i]:=b[i-1]+a[i];
f[1,i]:=b[i];
end;
for i:=2 to n do
for j:=i to m do
for k:=1 to j-1 do
f[i,j]:=min(max(f[i-1,k],b[j]-b[k]),f[i,j]);
print(m,n);
end.