动态规划练习题(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.
COGS上一位神牛的代码

08年的,作者maxiem,侵删。

转载于:https://www.cnblogs.com/vacation/p/5406398.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值