动态规划练习题(2)
1、完全背包(knapsack.pas)
AYYZOJ p1473
1 program p1473; 2 const 3 maxm=200; maxn=30; 4 var 5 i,j,n,m:integer; 6 w,u:array[1..maxn] of integer; 7 f:array[0..maxn,0..maxm] of integer; 8 begin 9 fillchar(w,sizeof(w),0); 10 fillchar(u,sizeof(u),0); 11 readln(m,n); 12 for i:=1 to n do readln(w[i],u[i]); 13 for i:=1 to n do 14 begin 15 for j:=1 to w[i]-1 do 16 f[i,j]:=f[i-1,j]; 17 for j:=w[i] to m do 18 if f[i-1,j]>f[i,j-w[i]]+u[i] then f[i,j]:=f[i-1,j] 19 else f[i,j]:=f[i,j-w[i]]+u[i]; 20 end; 21 writeln(f[n,m]); 22 end.
2、货币系统(money.pas)
AYYZOJ p1133
COGS p698
统计完全背包方案数
1 const 2 maxv=25; 3 var 4 v,n,i,j,k,t:longint; 5 a:array[1..maxv] of longint; 6 f:array[0..maxv,0..10000] of int64; 7 begin 8 assign(input,'moneysys.in'); reset(input); 9 assign(output,'moneysys.out'); rewrite(output); 10 readln(v,n); 11 for i:=1 to v do 12 read(a[i]); 13 for i:=0 to v do f[i,0]:=1; 14 for i:=1 to v do 15 for j:=1 to n do 16 begin 17 f[i,j]:=f[i-1,j]; 18 if j>=a[i] then 19 f[i,j]:=f[i,j]+f[i,j-a[i]]; 20 end; 21 writeln(f[v,n]); 22 close(input); close(output); 23 end.
1 var 2 v,n,i,j:longint; 3 m:array[0..26] of longint; 4 f:array[0..26,0..10000] of int64; 5 begin 6 assign(input,'moneysys.in');reset(input); 7 assign(output,'moneysys.out');rewrite(output); 8 readln(v,n); 9 for i:=1 to v do begin read(m[i]);f[i,0]:=1;end; 10 for i:=1 to v do 11 for j:=1 to n do 12 begin 13 f[i,j]:=f[i,j]+f[i-1,j]; 14 if j-m[i]>=0 then f[i,j]:=f[i,j]+f[i,j-m[i]]; 15 end; 16 writeln(f[v,n]); 17 close(input); 18 close(output); 19 end.
1 {设f[i,j]表示用前i种硬币能表示j面额货币的方法数:则f[i,j]=f[i-1,j]+f[i-1,j-a[i]];即前i种硬币能表示j货币的种类=不用第i种硬币能表示的j货币的种类+用上第i种货币能表示j货币的种类。当然这个方程在空间上还可以简化为一维的 2 } 3 program money; 4 var 5 n,v:integer; 6 cash:array[1.. 25] of integer; 7 f:array[0..10000] of qword; 8 procedure init; 9 var 10 i:integer; 11 begin 12 readln(v,n); 13 for i:=1 to v do read(cash[i]); 14 readln; 15 end; 16 procedure dp; 17 var 18 i,j,k,l:integer; 19 begin 20 fillchar(f,sizeof(f),0); 21 f[0]:=1; 22 for i:=1 to v do 23 for j:=cash[i] to n do 24 inc(f[j],f[j-cash[i]]); 25 writeln(f[n]); 26 end; 27 begin 28 init; 29 dp; 30 end.
[分析]
1 题意:已知v种货币,求用这v种货币构成面值n的种数。 2 解题思路: 3 用一维数组f[i]表示循环到当前构成面值i的种数 4 则f[i]为组成当前面值i的种数+组成I-a[j]面值的种数之和 5 例如:当前循环的货币为2元,构成面值8元共有a种,构成面值6元共有b种,则构成6元的每种方案加2元都能构成8元,f[8]:=f[8]+f[6]=a+b 6 动态转移方程为f[i]:=f[i]+f[I-a[j]],目标状态为f[n] 7 程序如下: 8 program p_1133; 9 var 10 f:array[0..10000]of int64; 11 a:array[0..30]of int64; 12 i,j,v,n:longint; 13 begin 14 read(v,n); 15 for i:=1 to v do 16 read(a[i]); 17 f[0]:=1; // 初始化,构成面值0共一种方案,即每种货币0张 18 for j:=1 to v do // 循环v种货币 19 for i:=1 to n do // 循环面值1..n 20 if i>=a[j] then 21 f[i]:=f[i-a[j]]+f[i]; 22 writeln(f[n]); 23 end.
3、质数和分解(prime.pas)
与第二题同类型
1 program p1474; 2 const maxp=200; 3 var 4 f:array[1..maxp] of boolean; 5 p:array[0..maxp] of longint; 6 n,i,j:longint; 7 dp:array[0..maxp,0..maxp] of longint; 8 procedure shai(maxp:longint); 9 var i,j:longint; 10 begin 11 for i:=2 to maxp do 12 if not f[i] then 13 begin 14 inc(p[0]); 15 p[p[0]]:=i; 16 for j:=2 to maxp div i do 17 f[i*j]:=true; 18 {j:=i; 19 while i+j<=maxp do begin 20 j:=j+i; f[j]:=true; 21 end; } 22 end; 23 end; 24 begin 25 readln(n); 26 shai(n); 27 {for i:=1 to n do 28 dp[0,i]:=1; } 29 for i:=1 to p[0] do 30 dp[i,0]:=1; 31 for i:=1 to p[0] do 32 for j:=1 to n do 33 begin 34 dp[i,j]:=dp[i-1,j]; 35 if j>=p[i] then dp[i,j]:=dp[i,j]+dp[i,j-p[i]]; 36 end; 37 writeln(dp[p[0],n]); 38 end.
1 program prime; {完全背包} 2 var i,j,k,n,m:longint; 3 f,a:array[0..1000000]of longint; 4 begin 5 readln(n); 6 k:=1; 7 a[k]:=2; 8 for i:=3 to n do {求n以内的质数} 9 begin 10 for j:=2 to i-1 do 11 if i mod j=0 then 12 break; 13 if i mod j<>0 then begin inc(k);a[k]:=i;end; 14 end; 15 f[0]:=1; {初始化,不能分解时为一种方案} 16 for i:=1 to k do 17 begin 18 for j:=1 to n do 19 if a[i]<=j then 20 f[j]:=f[j]+f[j-a[i]]; 21 end; 22 writeln(f[n]); 23 end.
老师给的程序
1 {方法一: 2 用F[J]表示j的质数和分解的个数 3 j=(j-i)+I 如果此处的I是质数 那么f[j-i]中的所有和分解情况在f[j]中也同样成立,即inc(f[j],f[j-i]) 4 数据弱到200,f开longint足够用,判断质数直接写成200的布尔数组 5 P.S. 注意数组的清空和f[0]的初值为1 6 } 7 8 const 9 m=46; 10 sushu:array[1..m] of integer=(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73, 11 79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179, 12 181,191,193,197,199); 13 var 14 su:array[1..200] of boolean; 15 f:array[0..200] of int64; 16 i,j,n:integer; 17 begin 18 readln(n); 19 fillchar(f,sizeof(f),0); 20 fillchar(su,sizeof(su),0); 21 for i:=1 to 46 do 22 su[sushu[i]]:=true; 23 f[0]:=1; 24 for i:=2 to n do 25 if su[i] then for j:=i to n do inc(f[j],f[j-i]); 26 writeln(f[n]); 27 end. 28 29 30 //方法二: 31 program prime; {完全背包(每个质数所用次数没有限制)} 32 var i,j,k,n,m:longint; 33 f,a:array[0..1000000]of longint; 34 begin 35 readln(n); 36 k:=1; 37 a[k]:=2; 38 for i:=3 to n do {求n以内的质数} 39 begin 40 for j:=2 to i-1 do 41 if i mod j=0 then 42 break; 43 if i mod j<>0 then begin inc(k);a[k]:=i;end; 44 end; 45 f[0]:=1; {初始化,不能分解时为一种方案} 46 for i:=1 to k do 47 begin 48 for j:=1 to n do 49 if a[i]<=j then 50 f[j]:=f[j]+f[j-a[i]]; 51 end; 52 writeln(f[n]); 53 end.
4、最小乘车费用(buses.pas)
AYYZOJ p1515
COGS p192
COGS p961(重复了)
类似于完全背包
1 分析: 2 本题是一道典型的完全背包问题。物品数已知,即可供选择的1-10km 的行驶路程;而题中的费用则相当于背包问题中的“价值”,题中的所走的里程数,即是背包问题中的“费用”。这样,这道题就完全转化为了我们所熟悉的完全背包。所以这道题也可以这样问:已知有10件物品,及每件物品的价值,和选择该物品的费用,每件物品可以选择多次,用一个固定的费用(即要行驶的里程),购买这10件物品,使它们的价值总和(即乘车费用)最小,输出可以达到的最小价值。这样该问题和能装满的完全背包就只有“max”和“min”的差别了。因为其中有行驶1km(费用为1)这种物品,所以要求的费用可以达到,这个背包也一定能够装满。 3 本题的关键是将题目转化为我们熟悉的问题——完全背包,另外初始化的问题也应该注意。 4 5 代码: 6 var 7 i,j,n:longint; 8 w,c:array[1..10] of longint; 9 dp:array[0..100] of longint; 10 function min(x,y:longint):longint; 11 begin if x<y then min:=x else min:=y; end; 12 begin 13 for i:=1 to 10 do 14 begin read(c[i]); w[i]:=i; end; 15 {读入这10件物品的“费用”和“价值”} 16 readln(n); 17 for i:=1 to n do dp[i]:=maxlongint; 18 {初始化,因为需要求的是最小费用,所以在比较时用的是min函数,若不初始化,则默认数组里的数都为0,将不能正常比较,也可以用fillchar(dp,sizeof(dp),$7F);dp[0]:=0} 19 for i:=1 to 10 do //依次枚举这10件物品 20 for j:=w[i] to n do 21 dp[j]:=min(dp[j],dp[j-w[i]]+c[i]); 22 writeln(dp[n]); 23 end.
1 program p1515; 2 var 3 n,i,j:longint; 4 w,v:array[1..10] of longint; 5 f:array[0..500] of longint; 6 function min(x,y:longint):longint; 7 begin 8 if x<y then exit(x) else exit(y); 9 end; 10 begin 11 for i:=1 to 10 do begin read(v[i]); w[i]:=i; end; 12 readln(n); 13 for i:=1 to n do f[i]:=100000; 14 f[0]:=0; 15 for i:=1 to 10 do 16 for j:=w[i] to n do 17 f[j]:=min(f[j],f[j-w[i]]+v[i]); 18 writeln(f[n]); 19 end.
1 var 2 i,j,k,n,m:longint; 3 f:array[0..500]of longint; 4 c:array[0..10]of longint; 5 begin 6 fillchar(f,sizeof(f),$7f); 7 f[0]:=0; 8 for i:=1 to 10 do read(c[i]); 9 readln(m); 10 for i:=1 to m do 11 for j:=1 to 10 do 12 if (i-j>=0) and (f[i-j]+c[j]<f[i]) then f[i]:=f[i-j]+c[j]; 13 writeln(f[m]); 14 end.
循环顺序不同。。省去一个数组
1 program busses; 2 var 3 f:array [1..10] of byte; 4 dp:array [1..320000] of dword; 5 n,i:word; 6 procedure go(n:dword); 7 var 8 i:dword; 9 tmp:dword; 10 begin 11 if n<>1 then begin 12 tmp:=maxint; 13 for i:=1 to (n-1) div 2+1 do begin 14 if dp[i]=0 then go(i); 15 if dp[n-i]=0 then go(n-i); 16 if dp[i]+dp[n-i]<tmp then tmp:=dp[i]+dp[n-i]; 17 end; 18 if n<=10 then if tmp>f[n] then tmp:=f[n]; 19 dp[n]:=tmp; 20 end; 21 end; 22 begin 23 fillchar (dp,sizeof(dp),0); 24 assign (input,'busses.in'); 25 reset (input); 26 for i:=1 to 10 do read (f[i]); 27 readln (n); 28 close (input); 29 assign (output,'busses.out'); 30 rewrite (output); 31 dp[1]:=f[1];go(n); 32 writeln (dp[n]); 33 close (output); 34 end.
08年的,作者maxiem,侵删。