• 【动态规划】01背包问题


      今天小编闲的不行,就打开洛谷,随便一打卡就是大吉,还宜刷题。

      正巧上午比赛时有一道背包问题,于是小编默默打开试炼场,瞅准了背包问题(别问我为什么),正所谓自知者明,小编也知道自己很水(建议看背包九讲),于是挑了三道题:

      在写之前总得知道什么是背包问题吧,背包问题一般长这样:

     题目:有N件物品和一个容量为V的背包。第i件物品的费用是w[i],价值是value[i]。求解将哪些物品装入背包可使价值总和最大。

      那么如何解这种题目呢,先定义一个数组f[i][j]为当一共有i件物品,背包容量为j时的最大价值。然后就要找状态转移方程了,小编以前总认为状态转移方程难写,但是只要一项一项列出来就好了。对于每一个物品,无非就两种可能:要么选,要么不选。那么先看选,凡是总有回报和代价把,选了之后代价是什么呢?想一想,是不是选了之后背包剩余容量就减少了;那么回报呢?当然是价值增加了呗。但是不选就不一样了,应为啥也没干,最大价值和之前一样,不增不减。这不就出来了嘛,两种情况如下:

    •   选:f[i][j]=f[i-1][j-w[i]]+value[i];
    •        不选:f[i][j]=f[i-1][j];

      因为题目求的是最大价值,所以两者中取大的就可以了,得到以下状态转移方程:

      f[i][j]=max(f[i-1][j-w[i]]+value[i],f[i-1][j]);

      有没有发现什么,我们用了二维数组,虽然时间上已经难以优化,但是空间上还是可以优化成一维数组的,只要同时去掉i的那一个维度就可以了,因为二维数组有太多不需要一直记录的,直接不断更新一维数组(滚动数组的方式)就可以了,更改如下:

      f[j]=max(f[j-w[i]]+value[i],f[j]);

      具体怎么实现,看几道吧。

      先看第一道:

     

      这道题处于秒杀的行列,直接用刚才的方法,把钱数看成是容量,把重要程度*价格看成是价值就好了,直接写就行,代码奉上:

     1 #include<iostream>
     2 using namespace std;
     3 long long cost[30000],w[30000],f[30000],ans;
     4 int main()
     5 {
     6     long long n,m;
     7     cin>>n>>m;
     8     for(int i=1;i<=m;i++)
     9     cin>>cost[i]>>w[i];
    10     for(int i=1;i<=m;i++)
    11     for(int j=n;j>=cost[i];j--)
    12     {
    13         if(j>=w[i])
    14         f[j]=max(f[j],f[j-cost[i]]+w[i]*cost[i]);
    15     }
    16     cout<<f[n];
    17     return 0;
    18 }

    先来看一下采药,比上面的还简单:

      把时间看成容量,就可以了,代码献上:

     1 #include<iostream>
     2 using namespace std;
     3 int t,m,w[1000],cost[1000],f[1000];
     4 int main()
     5 {
     6     cin>>t>>m;
     7     for(int i=1;i<=m;i++)
     8     cin>>w[i]>>cost[i];
     9     for(int i=1;i<=m;i++)
    10     for(int j=t;j>=w[i];j--)
    11     {
    12         f[j]=max(f[j],f[j-w[i]]+cost[i]);
    13     }
    14     cout<<f[t];
    15     
    16     return 0;
    17 }

    最后来看小A点菜:

      这道题乍一看没思路,还按照刚才的思路:要么吃,要么不吃,吃有什么代价,什么回报呢?钱变少了,方案数变多了呗;不吃呢?还是原来的方案数。这样两种情况就出来了:

    •   f[j-cost[i]];
    •        f[j]

       【注意】情况有变,这一次就不那么简单了,因为选和不选是两种不同的方案数,题目求的是一共的方案数,所以不是max,不是min,而是+。

      归根结底长这样:f[j]=f[j]+f[j-cost[i]];

       这样代码就出来了,代码呈上:

     1 #include<iostream>
     2 using namespace std;
     3 int n,m,cost[100000],f[10000];
     4 int main()
     5 {
     6     cin>>n>>m;
     7     for(int i=1;i<=n;i++)
     8     cin>>cost[i];
     9     f[0]=1;
    10     for(int i=1;i<=n;i++)
    11     for(int j=m;j>=cost[i];j--)
    12     f[j]=f[j]+f[j-cost[i]];
    13     cout<<f[m];
    14     return 0;
    15 }
  • 相关阅读:
    spring 动态创建数据源
    现有‘abcdefghijkl’12个字符,将其所有的排列按字典序进行排序,给出任意一组排列,说出这租排列在所有排列中是第几小的
    javaweb项目运行时错误
    StringUtils.isEmpty和StringUtils.isBlank用法和区别
    启动项目时tomcat问题汇总
    hibernate 在web.xml中配置的作用
    Java几种常见的编码方式
    struts学习总结
    Javaweb开发中关于不同地方出现的绝对路径和相对路径
    解决中文乱码问题
  • 原文地址:https://www.cnblogs.com/TFLS-gzr/p/10589576.html
Copyright © 2020-2023  润新知