书的复制记录方案DP

题目描述

书的复制
现在要把maxn本有顺序的书分给n个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。

输入
第一行两个整数maxn, n;(n<=maxn<=100)
第二行maxn个整数,第i个整数表示第i本书的页数。
输出
共n行,每行两个正整数,第i行表示第i个人抄写的书的起始编号和终止编号。n行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。

此题记录方案不同于一般DP,一般DP,按正常维护过程中记录一些量即可,但此题不可以,

因为方案要求是,抄尽可能少的页数,在此基础上,靠前的尽可能少抄,会发现,普通正序循环,在满足第一问的前提下,第二问不具有最优子结构,举个例子,输入:

10   4

1 1 1 1 1 1 1 1 1 1

最优情况最多的人三页,四个人抄写的页数分别为:1,3,3,3;

按照普通状态定义,f[i,j]表示前i个人抄j本书,抄的最多的抄多少,在维护f的过程中

维护一个ans[i,j],记录到达(i,j)这个状态,f最小的情况下,第i个人所拿的书是第几本开始(即第i个人拿第ans【i,j】到第j本书)

像普通DP一样,f更新一次,ans就更新一次,但这样会出问题

比如ans[2,4]=3,因为此时的最优情况就是第三个人拿后两本(f[2,4]=2一人两本),

这样在DP完之后,按路径把ans输出去,会出问题,因为此时还不知道后面人会怎么办

代码见下(不可AC):

program sky;
var
  i,j,k,m,n,tp:longint;
  a,g:array[0..101] of longint;
  f,ans:array[0..101,0..1001] of longint;
  print:array[0..101] of longint;
function max(qq,ww:longint):longint;
begin
  if qq>ww then exit(qq); exit(ww);
end;
begin
  assign(input,'book.in'); reset(input);
  assign(output,'book.out'); rewrite(output);
  readln(m,n);
  for i:=1 to m do begin read(a[i]); g[i]:=g[i-1]+a[i]; end;
  fillchar(f,sizeof(f),63);
  f[0,0]:=0;
  for i:=1 to n do
    for j:=0 to m do
      for k:=0 to j do
      begin
        tp:=max(f[i-1,k],g[j]-g[k]);
        if f[i,j]>tp then
        begin
          f[i,j]:=tp;
          ans[i,j]:=k+1;
        end;
      end;
  while j<>0 do
  begin
    print[i]:=ans[i,j];
    j:=ans[i,j]-1; dec(i);
  end;
  writeln(f[4,10]);
  if n<>0 then writeln(1,' ',print[2]-1);
  for i:=2 to n-1 do writeln(print[i],' ',print[i+1]-1);
  if n<>0 then writeln(print[n],' ',m);
  close(input); close(output);
end.

所以……我采用了一个比较朴素的办法,就是得到f之后,模拟装书……模拟部分码量大于DP,在此不再给出……不过网上也有直接DP记录方案的,状态表示需要稍作调整,本文重点在于指出,直接按上述状态记录方案,有可能会写出有后效性的代码,分享一下经验


 

希望大家参考一下bright的博客,但和直接模拟输出的结果是不一样的,在我手中的数据证明是bright的有误,他是直接DP记录方案的,希望众多神牛看出端倪,不管哪种解法有误,都提出来共同进步,蒟蒻代码如下:

program sky;
var
  a,g,p:array[0..10000] of longint;
  f:array[0..101,0..1001] of longint;
  tot,i,j,k,m,n,ans,tp:longint;
function min(qq,ww:longint):longint;
begin
  if qq>ww then exit(ww); exit(qq);
end;
function max(qq,ww:longint):longint;
begin
  if qq>ww then exit(qq); exit(ww);
end;
begin
  assign(input,'book.in'); reset(input);
  assign(output,'book.out'); rewrite(output);
  readln(m,n);
  for i:=1 to m do begin read(a[i]); g[i]:=g[i-1]+a[i]; end;
  fillchar(f,sizeof(f),63); f[0,0]:=0;
  for i:=1 to n do
    for j:=i to m do
      for k:=0 to j-1 do
        f[i,j]:=min(f[i,j],max(f[i-1,k],g[j]-g[k]));
  ans:=f[n,m];
  tot:=n;
  for i:=m downto 1 do
  begin
    if tp+a[i]>ans then
    begin
      p[tot]:=i+1;
      dec(tot);
      tp:=a[i];
    end else inc(tp,a[i]);
  end;
  p[n+1]:=m+1;
  if (n<>0) then writeln(1,' ',p[2]-1);
  for i:=2 to n do writeln(p[i],' ',p[i+1]-1);
  close(input); close(output);
end.
原文地址:https://www.cnblogs.com/skysun/p/2508053.html