zoukankan      html  css  js  c++  java
  • 动态规划练习题(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,侵删。

  • 相关阅读:
    k近邻算法
    密码技术小总结
    编码的简单总结
    icmp dos和arp dos 攻击模拟实验
    WPA简单抓包分析
    Iptables实验
    Rabin简单加解密
    response响应数据
    浮动——小米手机案例
    ServletRequest请求详解
  • 原文地址:https://www.cnblogs.com/vacation/p/5406398.html
Copyright © 2011-2022 走看看