Dp是一个经典算法,而背包问题在dp中是属于dp里的经典;
所以搞dp就是经典中的经典!
现在我这里提供经典的6种背包型dp和具体实现:
1、采药(原版01)https://www.luogu.org/problem/show?pid=1048
n株草药,每株有一个价值和采集时间。最多采集m个单位时间。求最大价值,n≤100,m≤1000
题解:这是经典的01背包问题,01背包是最最简单的一种背包~~
我们用f[i,j]表示前i种物品在j的时间内能够得到的最大价值,那么最最简单的f[0,0]=0
这我们称之为dp的边界!!!
对于一般的f[i,j],考虑第i个物品不选,那么f[i,j]=f[i-1,j];
考虑第i个物品选,那么f[i-1][j-v[i]]+w[i]该算式的实质是我们以v[i]为代价,换取w[i]的利润(好处)
综上,对于一般的经典01背包问题的经典递推式是:f[i,j]=max(f[i-1,j],f[i-1,j-v[i]]+w[i]) 边界是:f[0,0]=0;
pascal实现:
program O1Pack; uses math; var n,m,i,j,ans:longint; t,c:array[0..1000]of longint; f:array[0..1000,0..1000]of longint; begin readln(m,n); for i:=1 to n do readln(t[i],c[i]); fillchar(f,sizeof(f),0); for i:=1 to n do for j:=0 to m do begin f[i,j]:=f[i-1,j]; if j>=t[i] then f[i,j]:=max(f[i,j],f[i-1,j-t[i]]+c[i]); end; ans:=0; for i:=0 to m do ans:=max(ans,f[n,i]); writeln(ans); end.
优化:滚动数组优化到1维空间:
uses math; var i,j,v,n:longint; c,w:array[1..111]of longint; f:array[0..1111]of longint; begin readln(V,N); for i:=1 to n do readln(w[i],c[i]); for i:=1 to n do for j:=V downto w[i] do f[j]:=max(f[j],f[j-w[i]]+c[i]); writeln(f[V]); end.
2、采药(完全背包)
n种草药,每种有价值、采集时间,同种草药采集次数不限。最多采集M个单位时间。求最大价值。
n≤100,M≤1000
解:f[i][j]表示前i株中采集,用j个单位时间,最大价值。
f[i][j]=max(f[i-1][j],f[i][j-t[i]]+v[i])
uses math; Const maxn=10000;maxM=100000; var M,n,j,i:longint; w,v: array[1..maxn] of longint; F:array[0..maxM] of longint; //用滚动数组 begin read(M,n); for i:= 1 to n do read(w[i],v[i]); for i:=1 to n do for j:=w[i] to M do F[j]:=max(F[j],F[j-w[i]]+v[i]); writeln(F[M]); end.
3、采药(二维限制背包)
n种草药,每种有价值、采集时间和采集次数限制。最多采集M个单位时间。求最大价值。(每株只能采摘一次)
n≤100,M≤1000
解:
f[i,j]前i珠中花费时间小于等于j的草药的最大价值
w[]表示花费时间
c[]表示草药价值
t[]表示采摘次数
想到枚举k表示采集草药的次数来转移
f[i,j]=max(f[i-1,j],t);
t=max{f[i-1][j-k*w[i]]+k*c[i]}(0<=k<=t[i])
于是动态转移方程为
f[i,j]=max(f[i-1,j],=max{f[i-1][j-k*w[i]]+k*c[i]})(0<=k<=t[i]);
uses math; var n,m,i,j,k:longint; w,c,t:array[1..10000]of longint; f:array[0..100,0..1000]of longint; begin readln(n,m); for i:=1 to n do readln(w[i],c[i],t[i]); for i:=1 to n do for j:=1 to m do for k:=0 to t[i] do if j>=k*w[i] then f[i,j]:=max(f[i-1,j],f[i-1,j-k*w[i]]+c[i]*k) else f[i,j]:=f[i-1,j]; writeln(f[n,m]); end.
4、采药(二维费用) T5056
n株草药,每株有价值c[]、采集时间w[]、体力消耗p[]。最多采集M个单位时间,最多消耗t点体力值。求最大价值。
n≤100,M≤300,t≤300(每一件只能选一样或者每一件能选无数多)
input
5 100 200
10 20 30
5 25 35
50 70 100
1 1 1
125 25 4
output
166(300)
每一件只能选一次:
uses math; var n,m,t,i,j,k:longint; w,p,c:array[1..100000]of longint; f:array[0..100,0..300,0..300]of longint; begin readln(n,m,t); for i:=1 to n do readln(w[i],p[i],c[i]); for i:=1 to n do for j:=0 to m do for k:=0 to t do begin f[i,j,k]:=f[i-1,j,k]; if (w[i]<=j)and(p[i]<=k) then f[i,j,k]:=max(f[i,j,k],f[i-1,j-w[i],k-p[i]]+c[i]); end; writeln(f[n,m,t]); end.
每一件可以选任意多:
uses math; var n,m,t,i,j,k:longint; w,p,c:array[1..100000]of longint; f:array[0..100,0..300,0..300]of longint; begin readln(n,m,t); for i:=1 to n do readln(w[i],p[i],c[i]); for i:=1 to n do for j:=0 to m do for k:=0 to t do begin f[i,j,k]:=f[i-1,j,k]; if (w[i]<=j)and(p[i]<=k) then f[i,j,k]:=max(f[i,j,k],f[i,j-w[i],k-p[i]]+c[i]); end; writeln(f[n,m,t]); end.
5、分组背包 L1757
n株草药,每株有价值c[]、采集时间w[]、草药类别t[]。最多采集M个单位时间。相同类别的草药只能采一株,求最大价值。
n≤100,M≤1000
定义状态:f[i][j]表示在前i类中采集,用j单位时间的最大价值
状态转移:k枚举每一类,j枚举时间,i枚举每一类中的每一元素
为了方便起见,我们在处理类别的时候仿造邻接矩阵来存,a[t,0]为长度,a[t,i]为t种草药的第i号
状态转移与01背包有相似的地方:
选第k类的if[k-1,i]
采药⑹ 01背包输出方案问题
n株草药,每株有一个价值和采集时间。最多采集M个单位时间。求最大价值,输出任意一种方案。
n≤100,M≤1000
解:f[i][j]表示前i株中采集,用j个单位时间,最大价值。
f[i][j]=max(f[i-1][j],f[i-1][j-t[i]]+v[i])
g[i][j]表示g[i][j]是从f[i-1][j]还是f[i-1][j-t[i]]+v[i]转移过来。
从g[n][m]倒推回去,求出方案。
uses math; var n,m,i,j:longint; f:array[0..1000,0..1000]of longint; g:array[0..1000,0..1000]of boolean; w,c:array[0..10000]of longint; begin //g[i,j](bool) means f[i,j] change //by f[i-1,j] or f[i-1,j-t[i]]+v[i]? readln(m,n); for i:=1 to n do readln(w[i],c[i]); fillchar(g,sizeof(g),false); //false means change by f[i-1,j]; for i:=1 to n do for j:=1 to m do begin f[i,j]:=f[i-1,j]; if w[i]<=j then if f[i,j]<f[i-1,j-w[i]]+c[i] then begin f[i,j]:=f[i-1,j-w[i]]+c[i]; g[i,j]:=true; end; end; writeln(f[n,m]); while (m>0)and(n>0) do begin if g[n,m]then begin write(n,' '); m:=m-w[n]; dec(n); end else begin dec(n); end; end; writeln; end.