其实解决多次背包问题还可以用线性动规的方法(类似NOIP2009普及组道路游戏的算法)
设背包容量t,物品件数n,每件物品体积m[i],价值s[i],可用次数c[i](0表示无限制)
用 b[j,i] 记录到容量为j的情况下物品i使用次数,f[j] 记录容量为j的情况的最优解。
动规的时候,将j从1到t循环一次,分别求解f[j]
对于每个f[j],可以从f[j-m[i]] (i=1..n)得来
转移方程为f[j]=max(f[j-m[i]]+s[i]) (i=1..n, 且满足 j>=m[i] , b[j-m[i],i]<c[i])
且 b[j]=b[j-m[i]]; b[j,i]:=b[j,i]+1;
参考程序如下:
var
m,s,c:array[1..100] of longint;
f:array[0..100000] of longint;
b:array[0..100000,1..100] of longint;
n,t,i,now,max,j:longint;
begin
readln(n,t);
for i:=1 to n do
readln(m[i],s[i],c[i]);
for i:=1 to n do
if (c[i]=0)or(c[i]>t div m[i]) then c[i]:=t div m[i];
for j:=1 to t do
for i:=1 to n do
if (j>=m[i])and(b[j-m[i],i]<c[i])and(f[j-m[i]]+s[i]>f[j]) then
begin
f[j]:=f[j-m[i]]+s[i];
b[j]:=b[j-m[i]];
inc(b[j,i]);
end;
for i:=1 to t do
if f[i]>max then begin max:=f[i];end;
writeln(max);
end.
此方法相比单调队列优化,内存需求较大(辅助空间O(vn+v)),常数较大(b[j]:=b[j-m[i]]语句需要复制n个数组对象),但编程复杂度略低,容易理解。实际使用时若n较小则基本无区别
POJ1742
Time Limit: 3000MS | Memory Limit: 30000K | |
Total Submissions: 15923 | Accepted: 5530 |
Description
You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
Input
Output
Sample Input
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0
Sample Output
8 4
Source