题目描述
书的复制
现在要把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.