【区间动规】【记忆化搜索】[NOI1995]石子合并

题目:[NOI1995]石子合并 rqnoj490

题目描述

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

输入格式

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式

输出共2行,第1行为最小得分,第2行为最大得分.

样例输入

样例输出

动规 f[i,j]表示区间[i,j]

  f[i,j]=max{f[i,j],f[i,k]+f[k+1,j]+i 到 j 的总和,...} k从i到j-1循环

如果嫌递推麻烦的话可以记忆化搜索,很简单的

Pascal Code

program rqnoj490;

var
  n:longint;
  a,s:array[0..200+10] of longint;
  f:array[0..200+10,0..200+10] of longint;

procedure init;
begin
  assign(input,'rqnoj490.in');
  assign(output,'rqnoj490.out');
  reset(input);
  rewrite(output);
end;

procedure outit;
begin
  close(input);
  close(output);
  halt;
end;

procedure readdata;
var
  i:longint;
begin
  read(n);
  for i:=1 to n do
  begin
    read(a[i]);
    a[i+n]:=a[i];
  end;
  for i:=1 to 2*n do
    s[i]:=s[i-1]+a[i];
end;

function sum(i,j:longint):longint;
begin
  if i<=j then exit(s[j]-s[i-1])
  else exit(s[j+n]-s[i-1]);
end;

function min(i,j:longint):longint;
var
  k:longint;
begin
  if f[i,j]<>$07070707 then exit(f[i,j]);
  if i=j then exit(0);
  for k:=i to j-1 do
  begin
    if f[i,j]>min(i,k)+min(k+1,j)+sum(i,j) then
      f[i,j]:=min(i,k)+min(k+1,j)+sum(i,j);
  end;
  exit(f[i,j]);
end;

function max(i,j:longint):longint;
var
  k:longint;
begin
  if f[i,j]<>0 then exit(f[i,j]);
  if i=j then exit(0);
  for k:=i to j-1 do
  begin
    if f[i,j]<max(i,k)+max(k+1,j)+sum(i,j) then
      f[i,j]:=max(i,k)+max(k+1,j)+sum(i,j);
  end;
  exit(f[i,j]);
end;

procedure main;
var
  ans,i,j:longint;
begin
  ans:=maxlongint;
  fillchar(f,sizeof(f),$7);
  for i:=1 to n do
    if ans>min(i,i+n-1) then
      ans:=min(i,i+n-1);
  writeln(ans);
  ans:=0;
  fillchar(f,sizeof(f),0);
  for i:=1 to n do
    if ans<max(i,i+n-1) then
      ans:=max(i,i+n-1);
  writeln(ans);
end;

begin
  init;
  readdata;
  main;
  outit;
end.

 

posted @ 2012-08-19 17:45  jiangzh  阅读(414)  评论(0编辑  收藏  举报