1、动态规划分类1、背包模型 包括 0-1 背包、无限背包、有限背包、有价值背包、小数背包(贪心即可)等,是极为经典的模型,其转化与优化也是很重要的。 H P4 k v3T V A2、最长非降子序列模型 7i _ F+E N H g O8| | | s改版:渡河问题、合唱队型等 3、最大子段和模型 改版:K 大子段和、最佳游览,最大子矩阵和等。 4、LCS 模型 ?:p0n E m$v |+r改版:回文字串、多串的 LCS 等 5、括号序列模型 M Y3e V D:D改版:关灯问题(TSOJ)、charexp(TSOJ)、最大算式等,核心思想在于以串的长度为阶段。 6、递推模型 p:l s#U i
2、 _1J L 这类题是属于徘徊在 DP 与递归之间得一类题,本质是类似于记忆化搜索的一种填表,有很强的数学味。 const maxn=10;vara:array1maxn,1maxn of integer;max:longint;n,i,j:integer;fname:string;inputf:text;procedure try(x,y,dep:integer;sum:longint);beginif (dep=n) thenbeginif summax then max:=sum;exitend;try(x+1,y,dep+1,sum+ax+1,y);try(x+1,y+1,dep+1,
3、sum+ax+1,y+1);end;beginreadln(fname);assign(inputf,fname);reset(inputf);readln(inputf,n);for i:=1 to n dofor j:= 1 to i doread(inputf,ai,j);max:=0;try(1,1,1,a1,1);writeln(max);end.但是当行数很大时,当三角形的行数等于 100 时,其枚举量之大是可想而知的,用枚举法肯定超时,甚至根本不能得到计算结果,必须用动态规划法来解。二、怎样用动态规划法解题1.逆推法:按三角形的行划分阶段,若行数为 n,则可把问题看做一个 n-1
4、 个阶段的决策问题。先求出第 n-1 阶段(第 n-1 行上各点)到第 n 行的的最大和,再依次求出第 n-2 阶段、第 n-3 阶段第 1 阶段(起始点)各决策点至第 n 行的最佳路径。设:fi,j为从第 i 阶段中的点 j 至第 n 行的最大的数字和;则: fn,j=an,j 1fi+1,j+1 then fi,j:=ai,j+fi+1,jelse fi,j:=ai,j+fi+1,j+1;writeln(f1,1);end. 2. 顺推法按三角形的行划分阶段,若行数为 n,则可把问题看做一个 n-1 个阶段的决策问题。先求第 2 行各元素到起点的最大和,再依次求出第 3,4,5,.n-1,
5、n 到起点的最大和,最后找第 n 行的最大值设 fi,j为 第 i 行第 j 列上点到起点的最大和则 f1,1=a1,1;fi,1=ai, 1+fi-1,1;f i,j =max ai,j+fi-1,j-1,ai,j+fi-1,j 2fi-1,j then fi,j:=ai,j+fi-1,j-1else fi,j:=ai,j+fi-1,j;end;maxsum:=0;for i:=1 to n doif fn,imaxsum then maxsum:=fn,i;writeln(maxsum);end.说明一下:1.用动态规划解题主要思想是用空间换时间.2.本题如果 n 较大,用 2 维数组空间
6、可能不够,可以使用 1 维数组.程序如下:program datasjx;const maxn=100;varfname:string;inputf:text;n,i,j:integer;a:array1maxn,1maxn of integer;f:array1maxn of integer;maxsum:integer;beginreadln(fname);assign(inputf,fname);reset(inputf);readln(inputf,n);for i:=1 to n dofor j:=1 to i doread(inputf,ai,j);fillchar(f,sizeo
7、f(f),0);f1:=a1,1;for i:=2 to n dobeginfor j:=i downto 2 doif fj-1fj then fj:=ai,j+fj-1else fj:=ai,j+fj;f1:=ai,1+f1;end;maxsum:=0;for i:=1 to n doif fimaxsum then maxsum:=fi;writeln(maxsum);end.练习:用一维数组和逆推法解本题.三、用动态规划法解题的一般模式动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程
8、的一条活动路线(通常是求最优的活动路线)。如图所示。动态规划的设计都有着一定的模式,一般要经历以下几个步骤。 初始状态决策决策决策结束状态 (1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。(2)确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。(3)确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根
9、据相邻两段各状态之间的关系来确定决策。(4)寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。(5)程序设计实现:动态规划的主要难点在于理论上的设计,一旦设计完成,实现部分就会非常简单。根据上述动态规划设计的步骤,可得到大体解题框架如下:1.初始化(边界条件)2.for i:=2 to n (顺推法) 或 for i:=n-1 to 1(逆推法)对 i 阶段的每一个决策点求局部最优3.确定和输出结束状态的值.第三章 典型例题与习题3.1 最长不降子序列3.2 背包问题3.3 最短路径3.1 最长不降子序列(1)问题描述设有由 n 个不相同的整数组成的数列,记为:a
10、(1)、a(2)、a(n)且 a(i)j)例如 3,18,7,14,10,12,23,41,16,24。若存在 i1a(n)则存在长度为 1 的不下降序列 a(n-1)或 a(n)。3 一般若从 a(i)开始,此时最长不下降序列应该按下列方法求出:在 a(i+1),a(i+2),a(n)中,找出一个比 a(i)大的且最长的不下降序列,作为它的后继。4.用数组 b(i),c(i)分别记录点 i 到 n 的最长的不降子序列的长度和点 i 后继接点的编号(3) 程序如下:(逆推法)program li1;const maxn=100;var a,b,c:array1maxn of integer;f
11、name:string;f:text;n,i,j,max,p:integer;beginreadln(fname);assign(f,fname);reset(f);readln(f,n);+for i:=1 to n dobeginread(f,ai);bn:=1;cn:=0;end;for i:= n-1 downto 1 dobeginmax:=0;p:=0;for j:=i+1 to n doif (aimax) then begin max:=bj;p:=j end;if pmax then begin max:=bi;p:=i end;writeln(maxlong=,max);w
12、rite(result is:);while py then max:=x else max:=y;end;beginreadln(m,n);for i:= 1 to n doreadln(wi,ci);for i:=1 to m do f(0,i):=0;for i:=1 to n do f(i,0):=0; for i:=1 to n dofor j:=1 to m dobeginif j=wi then fi,j:=max(fi-1,j-wi+ci,fi-1,j)else fi,j:=fi-1,j;end;writeln(fn,m);end. 使用二维数组存储各子问题时方便,但当 max
13、m 较大时如 maxn=2000 时不能定义二维数组 f,怎么办,其实可以用一维数组,但是上述中 j:=1 to m 要改为 j:=m downto 1,为什么?请大家自己解决。 3.完全背包问题一个旅行者有一个最多能用 m 公斤的背包,现在有 n 种物品,每件的重量分别是 W1,W2,.,Wn,每件的价值分别为 C1,C2,.,Cn.若的每种物品的件数足够多.求旅行者能获得的最大总价值。本问题的数学模型如下: 设 f(x)表示重量不超过 x 公斤的最大价值, 则 f(x)=maxf(x-wi)+ci 当 x=wi 1=wj then t:=fi-wj+cj;if tfi then fi:=t
14、end;writeln(fm);end.3.3 最短路径问题描述:如图:求 v1 到 v10 的最短路径长度及最短路径。图的邻接矩阵如下:0 2 5 1 -1 -1 -1 -1 -1 -1-1 0 -1 -1 12 14 -1 -1 -1 -1-1 -1 0 -1 6 10 4 -1 -1 -1-1 -1 -1 0 13 12 11 -1 -1 -1-1 -1 -1 -1 0 -1 -1 3 9 -1-1 -1 -1 -1 -1 0 -1 6 5 -1-1 -1 -1 -1 -1 -1 0 -1 10 -1-1 -1 -1 -1 -1 -1 -1 0 -1 5-1 -1 -1 -1 -1 -1
15、 -1 -1 0 2-1 -1 -1 -1 -1 -1 -1 -1 -1 0采用逆推法设 f(x)表示点 x 到 v10 的最短路径长度则 f(10)=0f(x)=min f(i)+ax,i 当 ax,i0 ,x0) and (bj=wi 1=wi then m:=f(x-i)+ci;if mt then t:=m;end;f:=t;end;end;beginreadln(m,n);for i:= 1 to n doreadln(wi,ci);writeln(f(m);end.说明:当 m 不大时,编程很简单,但当 m 较大时,容易超时.4.2 改进的递归法改进的的递归法的思想还是以空间换时间
16、,这只要将递归函数计算过程中的各个子函数的值保存起来,开辟一个一维数组即可程序如下:program knapsack04;const maxm=2000;maxn=30;type ar=array0maxn of integer;var m,n,j,i,t:integer;c,w:ar;p:array0maxm of integer;function f(x:integer):integer;var i,t,m:integer;beginif px=wi then m:=f(i-wi)+ci;if mt then t:=m;end;px:=t;end;f:=px;end;end;beginre
17、adln(m,n);for i:= 1 to n doreadln(wi,ci);fillchar(p,sizeof(p),-1); writeln(f(m);end.4.3 习题 用改进的递归法解资源分配问题:n 个资源分配到 m 个项目上,i 项目分配 j 个资源可获益 ai,j,求最大总效益。5.1 例 1乘积最大问题问题描述:今年是国际数学联盟确定的“2000-世界数学年“,又恰逢我国著名数学家华罗庚先生诞辰 90 周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友 XZ 也有幸得以参加。活动中,主持人给所有参加活动的选手除了这样一道题目:设有一个
18、长度为 N 的数字串,要求选手使用 K 个乘号将它分成 K+1 个部分,找出一种分法,使得这 K+1 部分的乘积能够为最大。同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:有一个数字川:312,当 N=3,K=1 时会有一下两种分法:1) 3*12=362) 31*2=62这时,符合题目要求的结果是;31*2=62现在,请你帮助你的好朋友 XZ 设计一个程序,求得正确的答案。输入:程序的输入共有两行:第一行共有 2 个自然数 N,K(6N50,1K20)第二行是一个长度为 N 的数字串。输出:结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。样例:输入4 21
19、231输出62 程序如下:program tg20002;const maxn=50;maxk=20 ;type num=recordlen:byte;s:array1maxn of byte;end;type str=stringmaxn;var n,k,i,j,l:integer;st,temps:str;m,tempm:num;f:array1maxn of num;s1:array1maxn of str;procedure strtonum(str1:str;var num1:num);var i:integer;beginnum1.len:=length(str1);for i:=
20、1 to num1.len donum1.si:=ord(str1num1.len-i+1)-ord(0);end;procedure mult(x,y:num;var z: num );var i,j,len:integer;beginfillchar(z,sizeof(z),0);for i:= 1 to x.len dofor j:=1 to y.len dobegininc(z.si+j-1, x.si*y.sj);inc(z.si+j,z.si+j-1 div 10);z.si+j-1:=z.si+j-1 mod 10;end;len:=x.len+y.len+1;while (le
21、n1) and (z.slen=0) do len:=len-1;z.len:=len;end;procedure bigger(x,y:num;var z:num);var i:integer;beginif x.leny.len then begin z.len:=x.len;z.s:=x.s end elseif x.len1) and (x.si=y.si) do i:=i-1;if x.si=y.si then z.s:=x.s else z.s:=y.s;end;end;beginreadln(n,k);readln(st);fillchar(f,sizeof(f),0);for
22、i:=1 to n dobegins1i:=copy(st,1,i);strtonum(s1i,fi);end;for i:=2 to k+1 dofor j:=n downto i dobeginfillchar(m,sizeof(m),0);for l:=i-1 to j-1 dobegintemps:=copy(s1j,l+1,j-l);strtonum(temps,tempm);mult(fl,tempm, tempm);bigger(m,tempm,m);end;fj:=m;end;l:=fn.len;while (l1) and (fn.sl=0) do l:=l-1;for i:
23、=l downto 1 do write(fn.si);writeln;end.5.2 例 2统计单词个数 问题描述给出一个长度不超过 200 的由小写英文字母组成的字符串(约定:该字串以每行 20 个字母的方式输入,且保证每行一定为 20 个)。要求将此字串分成k 份(1length(wordj) then leni:=length(wordj);end;procedure calc_g;var i,j:integer;beginfillchar(g,sizeof(g),0);for i:=l downto 1 dobeginif leni=1 then gi,i:=1 ;for j:=l-
24、1 downto 1 doif lenjmaxt then maxt:=t;end;fj:=maxt;end;end;end;beginassign(fi,input3.dat);reset(fi);readln(fi,n);repeatdec(n);readln(fi,p,k);s1:=;for i:=1 to p dobeginreadln(fi,stri);s1:=s1+stri;end;l:=length(s1);readln(fi,s);for i:=1 to s doreadln(fi,wordi);calclen;calc_g;calc;writeln(fl);until n=0
25、;end.5.3 例 32002 年第四题:求:平面上的 n 个点用 k 个矩形覆盖的最小面积?程序如下:program tg20024;const maxn=50;type pointset=array1maxn,12 of longint;varn,k,i,j,l:integer;a:pointset;f:array1maxn of longint;maxs,min,t:longint;procedure init;var i:integer;filename:string20;f:text;beginreadln(filename);assign(f,filename);reset(f)
26、;readln(n,k);for i:= 1 to n doreadln(ai,1,ai,2);close(f);end;procedure sort(var x:pointset);var i,j,m:integer;t1,t2,t3:integer;beginfor i:=1 to n-1 dobegint1:=xi,1;t2:=xi,2;t3:=t2;m:=i;for j:= i+1 to n doif t3xj,2 then begin m:=j; t3:=xj,2;end;if mi thenbegin xi,1:=xm,1;xi,2:=xm,2;xm,1:=t1;xm,2:=t2
27、end;end;end;function maxy(x0,xn:integer):integer;var i,max:integer;beginmax:=0;for i:=x0 to xn doif maxai,1 then min:=ai,1;miny:=min;end;begininit;sort(a);fillchar(f,sizeof(f),0);maxs:=(an,2-a1,2)*(maxy(1,n)-miny(1,n);for i:= 1 to n dofi:=(ai,2-a1,2)*(maxy(1,i)-miny(1,i);for i:=2 to k dofor j:= n do
28、wnto 1 dobeginmin:=maxs;for l:=i-1 to j-1 dobegint:=fl+(aj,2-al+1,2)*(maxy(l+1,j)-miny(l+1,j);if (tal+1,2) then min:=t;end;fj:=min;end;writeln(fn);end.练习:Barn RepairIt was a dark and stormy night that ripped the roof and gates off the stalls that hold Farmer Johns cows. Happily, many of the cows wer
29、e on vacation, so the barn was not completely full. The cows spend the night in stalls that are arranged adjacent to each other in a long line. Some stalls have cows in them; some do not. All stalls are the same width. Farmer John must quickly erect new boards in front of the stalls, since the doors
30、 were lost. His new lumber supplier will supply him boards of any length he wishes, but the supplier can only deliver a small number of total boards. Farmer John wishes to minimize the total length of the boards he must purchase. Given M (1 =x) do dec(j);if i=j then minl:=j-i+1 else minl:=0;end;proc
31、edure calac_a;var i,j:integer;beginfor i:=1 to s dofor j:=1 to s doai,j:=minl(i,j);end;procedure main;var i,j,k,min:integer;beginfillchar(f,sizeof(f),0);calac_a;for i:=1 to s dofi:=a1,i;for i:= 2 to m dofor j:=s downto i dobeginmin:=200;for k:=i-1 to j-1 doif fk+ak+1,jmin then min:=fk+ak+1,j;fj:=min;end;end;beginassign(f1,barn1.in);reset(f1);readln(f1,m,s,c);fillchar(b,sizeof(b),false);for i:=1 to c dobeginreadln(f1,n);bn:=true;end;close(f1);assign(f2,barn1.out);main;rewrite(f2);writeln(f2,fs);close(f2);end.