zoukankan      html  css  js  c++  java
  • 背包6讲(例题)

    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.



  • 相关阅读:
    手把手教你在Android手机上实现蓝牙键盘的支持
    python 字符编码
    vi常用命令
    EditPlus for python
    Eclipse安装pydev插件
    手把手教你在Android手机上实现蓝牙键盘的支持
    2020年7月23日
    2020年7月20日
    2020年7月18日
    2020年7月22日
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/7200039.html
Copyright © 2011-2022 走看看