参考论文《对一类动态规划问题的研究》
《多角度思考创造性思维——运用树型动态规划解题的思路和方法探析》
这类动态规划的特点就是当前的决策会影响未来“行动”的费用,而我们并不能记录每个的所有决策
这时候我们就考虑在当前状态对未来状态预估,从而提前计算当前决策对未来影响的费用
下面的几道题目都很好,并且都能在这两篇论文中找到详细的题解,这里就贴代码不再赘述了
bzoj1812
不错的树dp,处理x为根子树的时候,我们预估x的某个祖先有伐木场时子树内的运费
详见《多角度思考创造性思维——运用树型动态规划解题的思路和方法探析》
1 const inf=2147483647; 2 type node=record 3 po,next,num:longint; 4 end; 5 6 var e:array[0..210] of node; 7 w,fa,p,d,g:array[0..110] of longint; 8 f:array[0..110,0..110,0..110] of longint; 9 i,j,k,len,x,y,n,m:longint; 10 11 procedure add(x,y,z:longint); 12 begin 13 inc(len); 14 e[len].po:=y; 15 e[len].num:=z; 16 e[len].next:=p[x]; 17 p[x]:=len; 18 end; 19 20 function min(a,b:longint):longint; 21 begin 22 if a>b then exit(b) else exit(a); 23 end; 24 25 procedure dfs(x:longint); 26 var i,y,z,mi:longint; 27 begin 28 if p[x]=0 then 29 begin 30 i:=fa[x]; 31 while i>-1 do 32 begin 33 f[x,i,0]:=(d[x]-d[i])*w[x]; 34 i:=fa[i]; 35 end; 36 f[x,x,1]:=0; 37 exit; 38 end; 39 i:=p[x]; 40 while i<>0 do 41 begin 42 y:=e[i].po; 43 d[y]:=d[x]+e[i].num; 44 dfs(y); 45 i:=e[i].next; 46 end; 47 i:=p[x]; 48 while i<>0 do 49 begin 50 y:=e[i].po; 51 z:=x; 52 while z>-1 do 53 begin 54 for j:=m downto 0 do 55 begin 56 mi:=inf; 57 for k:=0 to j do 58 mi:=min(mi,f[x,z,k]+f[y,z,j-k]); 59 f[x,z,j]:=mi; 60 end; 61 z:=fa[z]; 62 end; 63 i:=e[i].next; 64 end; 65 z:=fa[x]; 66 while z>-1 do 67 begin 68 for i:=0 to m do 69 if f[x,z,i]<inf then f[x,z,i]:=f[x,z,i]+(d[x]-d[z])*w[x]; 70 z:=fa[z]; 71 end; 72 fillchar(g,sizeof(g),0); 73 i:=p[x]; 74 while i<>0 do 75 begin 76 y:=e[i].po; 77 for j:=m downto 0 do 78 begin 79 mi:=inf; 80 for k:=1 to j do 81 mi:=min(mi,g[k]+f[y,x,j-k]); 82 g[j]:=mi; 83 end; 84 i:=e[i].next; 85 end; 86 z:=x; 87 while z>-1 do 88 begin 89 for i:=0 to m do 90 f[x,z,i]:=min(f[x,z,i],g[i]); 91 z:=fa[z]; 92 end; 93 end; 94 95 begin 96 readln(n,m); 97 for i:=1 to n do 98 begin 99 readln(w[i],fa[i],y); 100 add(fa[i],i,y); 101 end; 102 fa[0]:=-1; 103 dfs(0); 104 writeln(f[0,0,m]); 105 end.
bzoj1495
非常好的树dp,处理x为根子树的时候,预估x的祖先AB付费方式的大小情况从而计算影响
具体请见《多角度思考创造性思维——运用树型动态规划解题的思路和方法探析》
其中转换计算方式,对状态的压缩以及复杂度的分析都非常有价值,建议认真研究
1 const inf=1000000007; 2 var a,f:array[0..2050,0..2050] of longint; 3 d,b,c:array[0..2050] of longint; 4 n,m,x,y,i,j,k,p,l,r,ans:longint; 5 6 function min(a,b:longint):longint; 7 begin 8 if a>b then exit(b) else exit(a); 9 end; 10 11 function lca(a,b:longint):longint; 12 begin 13 while a<>b do 14 begin 15 if a>b then a:=a div 2 16 else b:=b div 2; 17 end; 18 exit(a); 19 end; 20 21 begin 22 readln(n); 23 m:=1 shl n; 24 for i:=1 to m do 25 read(b[i]); 26 for i:=1 to m do 27 read(c[i]); 28 for i:=2 to m*2-1 do 29 d[i]:=d[i div 2]+1; 30 readln; 31 for i:=1 to m-1 do 32 begin 33 for j:=i+1 to m do 34 begin 35 read(y); 36 x:=d[lca(i+m-1,j+m-1)]; 37 inc(a[i,x],y); 38 inc(a[j,x],y); 39 end; 40 readln; 41 end; 42 for i:=m to m*2-1 do 43 begin 44 x:=i-m+1; 45 for j:=0 to m-1 do 46 begin 47 f[i,j shl 1+b[x]]:=c[x]; 48 for k:=0 to n-1 do 49 begin 50 if (j and (1 shl k)>0) then y:=1 else y:=0; 51 inc(f[i,j shl 1+1-y],a[x,k]); 52 end; 53 end; 54 end; 55 for i:=1 to m-1 do 56 for j:=0 to m*2-1 do 57 f[i,j]:=inf; 58 59 for i:=m-1 downto 1 do 60 for j:=0 to 1 shl d[i]-1 do 61 for k:=0 to 1 shl (n-d[i]) do 62 begin 63 x:=j shl (n-d[i]+1) or k; 64 if k<1 shl (n-d[i])-k then y:=0 else y:=1; 65 for p:=0 to min(k,1 shl (n-d[i]-1)) do 66 begin 67 if k-p>1 shl (n-d[i]-1) then continue; 68 l:=((j+y*(1 shl d[i])) shl (n-d[i]))+p; 69 r:=((j+y*(1 shl d[i])) shl (n-d[i]))+(k-p); 70 f[i,x]:=min(f[i,x],f[i*2,l]+f[i*2+1,r]); 71 end; 72 end; 73 74 ans:=f[1,0]; 75 for i:=1 to m*2-1 do 76 ans:=min(ans,f[1,i]); 77 78 writeln(ans); 79 end.
bzoj1065
也是非常好的树dp,可以看《对一类动态规划问题的研究》,讲解的很详细
在做树dp的时候预估当前子树的跟x距1的深度所带来的影响(因为x的祖先的后继可能被修改连到1)
1 var f:array[0..70,0..70,0..70] of double; 2 g,c,b:array[0..70] of double; 3 p:array[0..70] of longint; 4 j,tmp,i,n,m,len:longint; 5 ans:double; 6 7 function max(a,b:double):double; 8 begin 9 if a>b then exit(a) else exit(b); 10 end; 11 12 function min(a,b:longint):longint; 13 begin 14 if a>b then exit(b) else exit(a); 15 end; 16 17 procedure dfs(x,d:longint); 18 var i,j,k,l:longint; 19 begin 20 for i:=2 to n do 21 if p[i]=x then dfs(i,d+1); 22 for l:=min(2,d) to d do 23 begin 24 for i:=2 to n do 25 if p[i]=x then 26 begin 27 for j:=m downto 0 do //树上背包注意转移顺序 28 for k:=j downto 0 do 29 f[x,j,l]:=max(f[x,j,l],f[x,k,l]+max(f[i,j-k,l+1],f[i,j-k,1])); 30 end; 31 for j:=0 to m do 32 f[x,j,l]:=f[x,j,l]+c[x]*b[l]; 33 end; 34 if d>1 then 35 begin 36 fillchar(g,sizeof(g),0); 37 for i:=2 to n do 38 if p[i]=x then 39 begin 40 for j:=m downto 0 do 41 for k:=j downto 0 do 42 g[j]:=max(g[j],g[k]+max(f[i,j-k,1],f[i,j-k,2])); 43 end; 44 for j:=1 to m do 45 f[x,j,1]:=g[j-1]+c[x]*b[1]; 46 end; 47 end; 48 49 function dp:double; 50 var i,j,k:longint; 51 begin 52 fillchar(g,sizeof(g),0); 53 for i:=2 to n do 54 if p[i]=1 then 55 begin 56 for j:=m downto 0 do 57 for k:=j downto 0 do 58 g[j]:=max(g[j],g[k]+f[i,j-k,1]); 59 end; 60 dp:=0; 61 for i:=0 to m-1 do 62 dp:=max(dp,g[i]); 63 if tmp=1 then dp:=max(dp,g[m]); //原来后继就是1就不用修改 64 end; 65 66 begin 67 readln(n,m,b[1]); 68 for i:=2 to n do 69 b[i]:=b[i-1]*b[1]; 70 for i:=1 to n do 71 read(p[i]); 72 for i:=1 to n do 73 read(c[i]); 74 len:=2; 75 i:=p[1]; 76 while i<>1 do //穷举环长 77 begin 78 fillchar(f,sizeof(f),0); 79 tmp:=p[i]; 80 p[i]:=1; 81 for j:=2 to n do 82 if p[j]=1 then dfs(j,1); 83 ans:=max(ans,(dp+c[1])/(1-b[len])); 84 p[i]:=tmp; 85 i:=p[i]; 86 inc(len); 87 end; 88 writeln(ans:0:2); 89 end.
bzoj2037
相对简单,这题不用新开状态,直接在当前状态加上对未来的影响即可
详见《对一类动态规划问题的研究》
1 var f:array[0..1010,0..1010,1..2] of longint; 2 w:array[0..1010,0..1010] of longint; 3 s,a,b,v:array[0..1010] of longint; 4 i,j,l,n,x0:longint; 5 6 procedure swap(var a,b:longint); 7 var c:longint; 8 begin 9 c:=a; 10 a:=b; 11 b:=c; 12 end; 13 14 procedure sort(l,r:longint); 15 var i,j,x:longint; 16 begin 17 i:=l; 18 j:=r; 19 x:=a[(l+r) shr 1]; 20 repeat 21 while a[i]<x do inc(i); 22 while x<a[j] do dec(j); 23 if not(i>j) then 24 begin 25 swap(a[i],a[j]); 26 swap(b[i],b[j]); 27 swap(v[i],v[j]); 28 inc(i); 29 dec(j); 30 end; 31 until i>j; 32 if l<j then sort(l,j); 33 if i<r then sort(i,r); 34 end; 35 36 function max(a,b:longint):longint; 37 begin 38 if a>b then exit(a) else exit(b); 39 end; 40 41 function cost(a,b:longint):longint; 42 begin 43 exit(a*b); 44 end; 45 46 begin 47 readln(n,x0); 48 for i:=1 to n do 49 read(a[i]); 50 for i:=1 to n do 51 read(b[i]); 52 for i:=1 to n do 53 read(v[i]); 54 sort(1,n); 55 for i:=1 to n do 56 s[i]:=s[i-1]+v[i]; 57 for i:=1 to n do 58 for j:=i to n do 59 w[i,j]:=s[n]-(s[j]-s[i-1]); 60 for i:=1 to n do 61 begin 62 f[i,i,1]:=b[i]-cost(abs(a[i]-x0),s[n]); 63 f[i,i,2]:=f[i,i,1]; 64 end; 65 for l:=2 to n do 66 for i:=1 to n-l+1 do 67 begin 68 j:=i+l-1; 69 f[i,j,1]:=b[i]+max(f[i+1,j,1]-cost(a[i+1]-a[i],w[i+1,j]),f[i+1,j,2]-cost(a[j]-a[i],w[i+1,j])); 70 f[i,j,2]:=b[j]+max(f[i,j-1,2]-cost(a[j]-a[j-1],w[i,j-1]),f[i,j-1,1]-cost(a[j]-a[i],w[i,j-1])); 71 end; 72 writeln(max(f[1,n,1],f[1,n,2])/1000:0:3); 73 end.
poj1390
区间dp,对于消去区间[i,j],我们预估后面会连接k个和区域j同色的方块来计算分数
详见《对一类动态规划问题的研究》
1 var f:array[0..201,0..201,0..201] of longint; 2 wh:array[0..201,0..201] of longint; 3 len,color,s,loc,a,maxl,sc:array[0..201] of longint; 4 l,w,i,j,k,r,t,n,q,p:longint; 5 6 function max(a,b:longint):longint; 7 begin 8 if a>b then exit(a) else exit(b); 9 end; 10 11 begin 12 readln(t); 13 for w:=1 to t do 14 begin 15 readln(r); 16 n:=0; 17 fillchar(s,sizeof(s),0); 18 fillchar(len,sizeof(len),0); 19 for i:=1 to r do 20 begin 21 read(a[i]); 22 if a[i]=a[i-1] then 23 inc(len[n]) 24 else begin 25 inc(n); 26 inc(s[a[i]]); 27 loc[n]:=s[a[i]]; 28 wh[a[i],s[a[i]]]:=n; 29 len[n]:=1; 30 color[n]:=a[i]; 31 end; 32 end; 33 fillchar(sc,sizeof(sc),0); 34 for i:=n downto 1 do 35 begin 36 maxl[i]:=sc[color[i]]; 37 inc(sc[color[i]],len[i]); 38 end; 39 fillchar(f,sizeof(f),0); 40 for i:=1 to n do 41 for k:=0 to maxl[i] do 42 f[i,i,k]:=sqr(len[i]+k); 43 for l:=1 to n-1 do 44 for i:=1 to n-l do 45 begin 46 j:=i+l; 47 for k:=0 to maxl[j] do 48 begin 49 f[i,j,k]:=f[i,j-1,0]+sqr(len[j]+k); 50 q:=loc[j]-1; 51 while q>0 do 52 begin 53 p:=wh[color[j],q]; 54 if p<i then break; 55 f[i,j,k]:=max(f[i,j,k],f[i,p,k+len[j]]+f[p+1,j-1,0]); 56 dec(q); 57 end; 58 end; 59 end; 60 writeln('Case ',w,': ',f[1,n,0]); 61 end; 62 end.