zoukankan      html  css  js  c++  java
  • 背包训练

    背包问题初始值:

      恰好装满: d[0]=0, d[1].d[2].d[3]....d[n-1]= 负无穷;

      尽可能的满: d[0].d[1].d[2].....d[n-1] = 0;

    背包九讲:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么 任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

    01背包:

      多个物品对于每个物品只能选择一次且每个物品两种选择: 放入背包(背包容量减少,包含的价值增加);不放(背包容量和价值就等于上一个物品的状态)。

      状态:dp[j]=max( dp[j] , dp[j-w[i]] + v[i] ); //j=w[i]...v

    完全背包:

      多个物品对于每种物品可以选择多次。

      可以通过转化成01背包来求解,类似于:dp[j]=max( dp[j] , dp[j-k*W[i]] + k*V[i] ); 这里K 为倍数;

      有时类似于:dp[k]=max( dp[k] , dp[k-w[j]] + v[j] ); //在K容量下能获得最大价值,j为一类物品

      有时类似于:f[v]=max( f[v] , f[v-cost]+weight ); //V=0...n

    二维费用背包:

      对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有 一个可付出的最大值(背包容量)。

    第 k 最优解:

    1. HDU 2546 01背包

    问题分析:

      a) 若饭卡钱数小于5则结果为:饭卡钱数。否则找出若干菜品的价格和(不包括最大价格菜品)最趋于饭卡价格-5,最大价格菜品留着最后减去(这样才能使得最终饭卡钱数最少)。

      b) 饭卡钱数-5 为背包容量,找出最大价格菜品(这里用sort排序,最后一个为最大价格),一直装入背包。结果为:饭卡钱数-最大价格-dp[饭卡钱数-5]

      dp[i] 表示:容量为 i 时 所拥有的最大钱数。在该题中表示 当钱数为 i 时 最接近 i 钱数的钱数。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int dp[10000+6],a[1005];
     4 int main()
     5 {
     6     int n,m;
     7     while(scanf("%d",&n)!=EOF&&n)
     8     {
     9         memset(dp,0,sizeof(dp));
    10         memset(a,0,sizeof(a));
    11         for(int i=0; i<n; i++)
    12             scanf("%d",&a[i]);
    13         scanf("%d",&m);
    14         sort(a,a+n);
    15         for(int i=0; i<n-1; i++)
    16             for(int j=m-5; j>=a[i]; j--)
    17                 dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
    18         if(m<5)
    19             printf("%d
    ",m);
    20         else
    21             printf("%d
    ",m-dp[m-5]-a[n-1]);
    22     }
    23     return 0;
    24 }
    View Code

     2. HDU 1114 完全背包

    问题分析:

      最终要使得 放入的钱的重量(放钱后的重量-空罐的重量时) 钱数最小。

      dp[i] 表示:容量为 i 时 所拥有的最小钱数。在该题中表示 当容量为 钱的重量(=放钱后的重量-空罐的重量)时最少价值。

      注意在初始化时 dp[0]=0,没有重量即没有价值。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int dp[10000+6],v[505],w[505];
     4 int main()
     5 {
     6     int T,s,e,tn,i,j;
     7     scanf("%d",&T);
     8     while(T--)
     9     {
    10         memset(v,0,sizeof(v));
    11         memset(w,0,sizeof(w));
    12         scanf("%d%d%d",&s,&e,&tn);
    13         for(i=0; i<e; ++i)
    14             dp[i]=1<<30;
    15         dp[0]=0;
    16         for(i=1; i<=tn; ++i)
    17             scanf("%d%d",&v[i],&w[i]);
    18         for(i=1; i<=tn; ++i)
    19             for(j=w[i]; j<=e-s; ++j)
    20                 dp[j]=min(dp[j],dp[j-w[i]]+v[i]);
    21         if(dp[e-s]==1<<30)
    22             printf("This is impossible.
    ");
    23         else
    24             printf("The minimum amount of money in the piggy-bank is %d.
    ",dp[e-s]);
    25     }
    26     return 0;
    27 }
    View Code

     3. HDU 2191 完全背包

    问题分析: 

      dp[i] 表示 在容量为 i 时 能放的最大重量。在该题中表示 当钱为 i 时 能买得最多大米的重量。

      这里注意   可买的大米袋数 <=  商家拥有的袋数

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int v[105],w[105],num[105],dp[105];
     4 int main()
     5 {
     6     int T,n,m,i,j,k;
     7     scanf("%d",&T);
     8     while(T--)
     9     {
    10         memset(dp,0,sizeof(dp));
    11         scanf("%d%d",&n,&m);
    12         for(i=0; i<m; i++)
    13             scanf("%d%d%d",&v[i],&w[i],&num[i]);
    14         for(i=0; i<m; i++)
    15             for(j=n; j>=v[i]; j--)
    16                 for(k=1; k<=j/v[i]; k++)
    17                     if(k<=num[i])
    18                         dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
    19         printf("%d
    ",dp[n]);
    20     }
    21     return 0;
    22 }
    View Code

     4. HDU 2602 01背包

    问题分析:01背包练习基本题目*

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int v[1005],w[1005],dp[1005];
     4 int main()
     5 {
     6     int T,n,m,i,j,k;
     7     scanf("%d",&T);
     8     while(T--)
     9     {
    10         memset(dp,0,sizeof(dp));
    11         scanf("%d%d",&n,&m);
    12 
    13         for(i=0; i<n; i++)
    14             scanf("%d",&v[i]);
    15         for(i=0; i<n; i++)
    16             scanf("%d",&w[i]);
    17 
    18         for(i=0; i<n; i++)
    19             for(j=m; j>=w[i]; j--)
    20                 dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    21         printf("%d
    ",dp[m]);
    22     }
    23     return 0;
    24 }
    View Code

     5. HDU 2159 二维费用背包

    问题分析:

      dp[i][j] 表示 i 的容量 j 数目下的最大价值。在该题中表示 i 忍耐度下杀 j 个怪物所达到的最大经验值。 

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int ad[105],su[105],dp[105][105];
     4 int main()
     5 {
     6     int n,m,k,s,i,j,l;
     7     while(scanf("%d%d%d%d",&n,&m,&k,&s)!=EOF)
     8     {
     9         memset(dp,0,sizeof(dp));
    10         int ans=0;
    11 
    12         for(i=0; i<k; i++)
    13             scanf("%d%d",&ad[i],&su[i]);
    14         for(i=0; i<k; i++)
    15             for(j=1; j<=s; j++)
    16                 for(l=su[i]; l<=m; l++)
    17                 {
    18                     dp[l][j]=max(dp[l][j],dp[l-su[i]][j-1]+ad[i]);
    19                     if(dp[l][j]>=n)
    20                         ans=max(ans,m-l);
    21                 }
    22 
    23         if(dp[m][s]<n)
    24             printf("-1
    ");
    25         else
    26             printf("%d
    ",ans);
    27     }
    28     return 0;
    29 }
    View Code

     6. HDU 2955 01背包

    问题分析:

      dp[i] 表示 i 的容量下 的最大价值。 在该题中表示 在获得 i 的钱数下 能够逃跑的最大概率。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 struct P
     4 {
     5     int money;
     6     double opportunity;
     7 } p[105];
     8 int main()
     9 {
    10     int T,n,i,j,sum;
    11     double dp[10000];
    12     scanf("%d",&T);
    13     while(T--)
    14     {
    15         double t;
    16         sum=0;
    17         scanf("%lf%d",&t,&n);
    18         for(i=0; i<n; i++)
    19         {
    20             scanf("%d%lf",&p[i].money,&p[i].opportunity);
    21             sum+=p[i].money;
    22         }
    23 
    24         for(i=0; i<=sum; i++)dp[i]=0.0;
    25         dp[0]=1;
    26 
    27         for(i=0; i<n; i++)
    28             for(j=sum; j>=p[i].money; j--)
    29                 dp[j]=max(dp[j],dp[j-p[i].money]*(1-p[i].opportunity));
    30 
    31         for(i=sum; i>=0; i--)
    32         {
    33             if(dp[i]>=1-t)
    34             {
    35                 printf("%d
    ",i);
    36                 break;
    37             }
    38         }
    39     }
    40     return 0;
    41 }
    View Code

     7. HDU 1712 分组背包

    问题: 给你n门课程 m天复习时间

    2 2
    1 2
    1 3             输出:3

    两门课程,复习1天 利益值各为 1,复习两天 利益值分别为 2 3。问在出的复习天数能获得最大的利益。

    问题分析:  

      dp[i] 表示在 i 天可以获得的最大利益。

      状态方程:dp[j]=max(dp[j],dp[j-k]+s[i][k]); //减去相应的天数,加上相应的价值。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 int s[105][105],dp[1005];
     6 int main()
     7 {
     8     int n,m,i,j,k;
     9     while(scanf("%d%d",&n,&m)!=EOF&&n&&m)
    10     {
    11         memset(dp,0,sizeof(dp));
    12         for(i=1; i<=n; i++)
    13             for(j=1; j<=m; j++)
    14                 scanf("%d",&s[i][j]);
    15         for(i=1; i<=n; i++)
    16             for(j=m; j>=1; j--)
    17                 for(k=1; k<=j; k++)
    18                     dp[j]=max(dp[j],dp[j-k]+s[i][k]);
    19         printf("%d
    ",dp[m]);
    20     }
    21     return 0;
    22 }
    View Code

     8. HDU 2063 完全背包

    问题分析: 

      在本题中由于股票都是1000的价格,这里全部(总钱数、每种类型的本金数,利息不变)都 除以1000 ,不影响最终结果。

      dp[i]表示  i 本金下能够获得的最大利息数 。

     1 #include<cstdio>
     2 #include<string.h>
     3 #include<algorithm>
     4 using namespace std;
     5 int bj[50005],lx[50005],dp[50005];
     6 int main()
     7 {
     8     int T,money,year,type,i,j,k;
     9     scanf("%d",&T);
    10     while(T--)
    11     {
    12         scanf("%d%d%d",&money,&year,&type);
    13         for(i=1; i<=type; ++i)
    14         {
    15             scanf("%d%d",&bj[i],&lx[i]);
    16             bj[i]/=1000;
    17         }
    18         for(i=1; i<=year; i++)
    19         {
    20             int mm=money/1000;
    21             memset(dp,0,sizeof(dp));
    22             for(j=1; j<=type; j++)
    23                 for(k=bj[j]; k<=mm; k++)
    24                     dp[k]=max(dp[k],dp[k-bj[j]]+lx[j]);
    25             money+=dp[mm];
    26         }
    27         printf("%d
    ",money);
    28     }
    29     return 0;
    30 }
    View Code

     9. HDU 1171 01背包

    问题分析:

      要想使的两边的包重量接近相同,需得到全部重量,然后01背包 使其无线接近(对‘每’个价值的机器要么取要么不取)总重量的一半(sum/2,无限接近该值,则该值一定小于等于真实的sum的一半,所以输出sum-dp[sum/2],dp[sum/2])即可。

     1 #include<cstdio>
     2 #include<string.h>
     3 #include<algorithm>
     4 using namespace std;
     5 int num[100005],v[100005],dp[100005];
     6 int main()
     7 {
     8     int n,i,j,sum,k,tem;
     9     while(scanf("%d",&n)!=EOF&&n>=0)
    10     {
    11         sum=0;
    12         k=0;
    13         for(i=0; i<n; i++)
    14         {
    15             scanf("%d%d",&tem,&num[i]);
    16             sum+=tem*num[i];
    17             for(j=0; j<num[i]; j++)
    18                 v[k++]=tem;
    19          }
    20         memset(dp,0,sizeof(dp));
    21         for(i=0; i<k; i++)
    22             for(j=sum/2; j>=v[i]; j--)
    23                 dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
    24         printf("%d %d
    ",sum-dp[sum/2],dp[sum/2]);
    25     }
    26     return 0;
    27 }
    View Code

     10. HDU 2639 背包第k优解

    问题分析:

      在01背包的基础上,

     1 #include<cstdio>
     2 #include<string.h>
     3 #include<algorithm>
     4 using namespace std;
     5 int w[1005],v[1005],dp[10005][35];
     6 int a[50],b[50];
     7 int main()
     8 {
     9     int T,i,j,k,n,ww,num;
    10     scanf("%d",&T);
    11     while(T--)
    12     {
    13         scanf("%d%d%d",&n,&ww,&num);
    14         for(i=0; i<n; i++)
    15             scanf("%d",&v[i]);
    16         for(i=0; i<n; i++)
    17             scanf("%d",&w[i]);
    18         memset(dp,0,sizeof(dp));
    19         for(i=0; i<n; i++)
    20             for(j=ww; j>=w[i]; j--)
    21             {
    22                 for(k=1; k<=num; k++)
    23                 {
    24                     a[k]=dp[j][k],b[k]=dp[j-w[i]][k]+v[i];
    25                 }
    26                 int x,y,z;
    27                 x=y=z=1;
    28                 a[num+1]=b[num+1]=-1;
    29                 while(z<=num&&(x<=num||y<=num))
    30                 {
    31                     if(a[x]>b[y])
    32                         dp[j][z]=a[x++];
    33                     else
    34                         dp[j][z]=b[y++];
    35                     if(dp[j][z]!=dp[j][z-1])
    36                         z++;
    37                 }
    38             }
    39         printf("%d
    ",dp[ww][num]);
    40     }
    41     return 0;
    42 }
    View Code

     11. vijos 1412 第K优解

     1 #include<cstdio>
     2 using namespace std;
     3 int vv[10005],ww[10005],dp[10005][60];
     4 int a[1004],b[1004];
     5 int main()
     6 {
     7     int k,v,n,i,j,l;
     8     while(scanf("%d%d%d",&k,&v,&n)!=EOF)
     9     {
    10         for(i=0; i<n; i++)
    11             scanf("%d%d",&ww[i],&vv[i]);
    12 
    13         for(i=0; i<v; i++)
    14             for(j=0; j<k; j++)
    15                 dp[i][j]=-1e5;
    16         dp[0][0]=0;
    17 
    18         for(i=0; i<n; i++)
    19             for(j=v; j>=ww[i]; j--)
    20             {
    21                 for(l=0; l<k; l++)
    22                 {
    23                     a[l]=dp[j][l];
    24                     b[l]=dp[j-ww[i]][l]+vv[i];
    25                 }
    26                 int x=0,y=0,z=0;
    27                 while(z<k)
    28                 {
    29                     if(a[x]>b[y])
    30                         dp[j][z]=a[x++];
    31                     else
    32                         dp[j][z]=b[y++];
    33                     z++;
    34                 }
    35             }
    36         int ans=0;
    37         for(i=0; i<k; i++)
    38             ans+=dp[v][i];
    39         printf("%d
    ",ans);
    40     }
    41     return 0;
    42 }
    View Code

      

  • 相关阅读:
    SQL server中自定义排序
    安装nodejs版本模块报错notsup Unsupported platform for n
    vue项目中一些标签直接放在<template>下会报错Failed to compile with 1 errors
    vue中使用element-ui出现Couldn't find preset "es2015" relative to directory
    解决两个相邻的span,或者input和button中间有间隙,在css中还看不到
    VsCode中代码折叠快捷键
    npm 操作代码
    vue项目打包成html,在本地点击直接能打开
    地图只显示部分区域,其他地区不显示
    vs里颜色显示块怎样显示
  • 原文地址:https://www.cnblogs.com/A--Q/p/6697981.html
Copyright © 2011-2022 走看看