• DP之背包经典三例


    0/1背包 HDU2602

    01背包(ZeroOnePack): 有N件物品和一个容量为V的背包,每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。 

    这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。 

    用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是: 

    f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

    把这个过程理解下

    在前i件物品放进容量v的背包时,它有两种情

    情况一: 第i件不放进去,这时所得价值为:f[i-1][v]

    情况二: 第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i] 

    (第二种是什么意思?就是如果第i件放进去,那么在容量v-c[i]里就要放进前i-1件物品) 

    最后比较第一种与第二种所得价值的大小,哪种相对大,f[i][v]的值就是哪种。  (这里是重点,理解!) 

    这里是用二维数组存储的,可以把空间优化,用一维数组存储。 

    用f[0..v]表示,f[v]表示把前i件物品放入容量为v的背包里得到的价值。把i从1~n(n件)循环后,最后f[v]表示所求最大值。


    这里f[v]就相当于二维数组的f[i][v]。那么,如何得到f[i-1][v]和f[i-1][v-c[i]]+w[i]?(重点!思考)

    首先要知道,我们是通过i从1到n的循环来依次表示前i件物品存入的状态。

    即:for i=1..N

    现在思考如何能在是f[v]表示当前状态是容量为v的背包所得价值,而又使f[v]和f[v-c[i]]+w[i]标签前一状态的价值? 

    逆序

    这就是关键! 

    123 for i=1..N for v=V..0 f[v]=max{f[v],f[v-c[i]]+w[i]};

    分析上面的代码:当内循环是逆序时,就可以保证后一个f[v]和f[v-c[i]]+w[i]是前一状态的!这里给大家一组测试数据:  测试数据: 10,3 3,4 4,5 5,6 

    01pack-1

    图2: 01背包图(1)

    这个图表画得很好,借此来分析: 

    C[v]从物品i=1开始,循环到物品3,期间,每次逆序得到容量v在前i件物品时可以得到的最大值。

    Bone Collector


    Problem Description
    Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
    The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?


    Input
    The first line contain a integer T , the number of cases.
    Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.


    Output
    One integer per line representing the maximum of the total value (this number will be less than 231).


    Sample Input
    15 101 2 3 4 55 4 3 2 1


    Sample Output
    14
     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 int main()
     5 {
     6     int t;
     7     int pack, maxv;
     8     int weight[1005], value[1005];
     9     int record[1005];
    10     scanf("%d", &t);
    11     while (t--)
    12     {
    13         memset(record, 0, sizeof(record));
    14         scanf("%d %d", &pack, &maxv);
    15         for (int i = 0; i < pack; i++)
    16             scanf("%d", &value[i]);
    17         for (int i = 0; i < pack; i++)
    18             scanf("%d", &weight[i]);
    19         for(int i=0;i<pack;i++)
    20             for (int j = maxv; j >= weight[i]; --j)
    21             {
    22                 if (record[j - weight[i]] + value[i] > record[j])
    23                     record[j] = record[j - weight[i]] + value[i];
    24             }
    25         printf("%d
    ", record[maxv]);
    26     }
    27     return 0;
    28 }

    完全背包 HDU 1248

    寒冰王座

    Problem Description
    不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店前.

    死亡骑士:"我要买道具!"

    地精商人:"我们这里有三种道具,血瓶150块一个,魔法药200块一个,无敌药水350块一个."

    死亡骑士:"好的,给我一个血瓶."

    说完他掏出那张N元的大钞递给地精商人.

    地精商人:"我忘了提醒你了,我们这里没有找客人钱的习惯的,多的钱我们都当小费收了的,嘿嘿."

    死亡骑士:"......"

    死亡骑士想,与其把钱当小费送个他还不如自己多买一点道具,反正以后都要买的,早点买了放在家里也好,但是要尽量少让他赚小费.

    现在死亡骑士希望你能帮他计算一下,最少他要给地精商人多少小费.


    Input
    输入数据的第一行是一个整数T(1<=T<=100),代表测试数据的数量.然后是T行测试数据,每个测试数据只包含一个正整数N(1<=N<=10000),N代表死亡骑士手中钞票的面值.

    注意:地精商店只有题中描述的三种道具.


    Output
    对于每组测试数据,请你输出死亡骑士最少要浪费多少钱给地精商人作为小费.


    Sample Input
    2
    900
    250


    Sample Output
    0
    50
     1 //解法一:纯粹暴力 时间复杂度 O(n^3) n=100;
     2 #include<stdio.h>
     3  int main()
     4 {
     5   int t,n,i,j,k;
     6   int price;
     7   scanf("%d",&t);
     8   while(t--)
     9 {
    10   int max=0;
    11   scanf("%d",&n);
    12   for(i=0;i<100;i++)
    13   for(j=0;j<100;j++)
    14   for(k=0;k<100;k++)
    15 {
    16   price=150*i+200*j+350*k;
    17   if(price<=n&&price>=max)
    18   max=price;
    19 }
    20   printf("%d
    ",n-max);
    21 }
    22 return 0;
    23 }
    24 
    25 //解法二:DP完全背包
    26 #include<stdio.h>
    27 #include<string.h>
    28 int max(int a,int b)
    29 {
    30 return a>b?a:b;
    31 }
    32 int main()
    33 {
    34 int i,j,m,n;
    35 int dp[10001];
    36 int a[3]={150,200,350};
    37 scanf("%d",&n);
    38 while(n--)
    39 {
    40 scanf("%d",&m);
    41 memset(dp,0,sizeof(dp));
    42 for(i=0;i<3;i++)
    43 {
    44 for(j=a[i];j<=m;j++)
    45 {
    46 dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
    47 }
    48 }
    49 printf("%d
    ",m-dp[m]);
    50 }
    51 return 0;
    52 }
    多重背包 HDU2191
    Problem Description
    急!灾区的食物依然短缺!
    为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。
    请问:你用有限的资金最多能采购多少公斤粮食呢?

    Input
    输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。

    Output
    对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。

    Sample Input
    18 22 100 44 100 2

    Sample Output
     
    400
     1  #include<stdio.h>
     2 #include<string.h>
     3 int main()
     4 {
     5     int dp[150];
     6     int p[150],h[150],c[150];
     7     int C;
     8     scanf("%d",&C);
     9     while(C--)
    10     {
    11         memset(dp,0,sizeof(dp));
    12         int m,n;
    13         scanf("%d %d",&n,&m);//两个整数n和m分别表示经费的金额和大米的种类
    14         for(int i=0;i<m;i++)
    15         scanf("%d %d %d",&p[i],&h[i],&c[i]);//每袋的价格、每袋的重量以及对应种类大米的袋数
    16         for(int i=0;i<m;i++)
    17             for(int j=0;j<c[i];j++)
    18                 for(int k=n;k>=p[i];--k)
    19                     if(dp[k]<dp[k-p[i]]+h[i])
    20                         dp[k]=dp[k-p[i]]+h[i];
    21         printf("%d
    ",dp[n]);
    22     }
    23     return 0;
    24  }
    多重背包只要在完全背包的基础上加一个循环,把j=0~bag[i],表示把第i钟背包从0枚举到bag[i]件即可。

     

  • 相关阅读:
    JDBC遇到向ORACLE数据库表执行插入操作时,报错“列在此处不允许”
    关于对称加密和非对称加密以及签名,认证和证书的理解
    .net framework 各版本区别
    数据库设计三大范式
    业务系统设计
    修改 Windows 服务器默认远程端口3389
    iis读取不到本地证书问题 提示已经导入成功
    HTTPS 建立连接的详细过程
    使用ServiceStack构建Web服务
    转-微信支付(公众号支付JSAPI)
  • 原文地址:https://www.cnblogs.com/YingZhixin/p/6496244.html
Copyright © 2020-2023  润新知