5月8日一周学习总结
背包问题
这周开始练习背包问题,背包问题本质是dp问题的一个分支,但由于过于经典而被列了出来,其中背包问题还可以分为01背包,完全背包,多重背包等,背包问题的重点在于寻找动态转移方程,背包问题模板:给定一个体积为V的背包,再给出多个物品的体积和价值,求装满背包时最多能装多少价值的东西。其中01背包是规定每样物品只有一样,也即对于一种物品只有两种情况,要么装入背包。要么不装入,如果装入的话,则要寻找子问题,也就是装入这个物品之后,剩余的体积最多装多少物品。完全背包问题则是一个物品可以被挑选任意多个,多重背包则是一个物品有有限个供挑选。
可以用一道题目来做具体解释,给定背包体积为10,给出5个物品的价值和体积,求装满背包时的最大价值。
如图是使用二维数组的做法,(i表示第i个物品,j表示此时占用的体积,v[i]表示当前物品的体积,value[i]表示当前物品的价值)此时a[i][[j]=max(a[i-1][j],a[i-1][j-v[i]]+value[i],这种方法理论上是可行的,但是在实际操作的时候有可能会出现运行超时的情况,所以需要简化代码,这时候可以采用滚动数组的方法,用一维数组来解题。
for(int i=1;i<=n;i++)
{
for(int j=v;j>=v2[i];j--)
{
bag[j]=max(bag[j],bag[j-v2[i]]+v1[i]);
}
}
这里的第二重循环之所以采用倒序的方式是因为这个时候前面的背包数据还未更新,仍保留了上次循环的数据,如果从前开始则会出现一个物品被多次放入背包的现象,这也是01背包与完全背包的一个重要区别
背包问题例题
HDU 2955 Robberies(一道个人觉得很有意思的题目)Roy打算抢劫银行。有N个银行,每个银行有存款Mi,和被抢劫后抓住的概率Pi。不同银行被抓的概率是独立的。现在Roy希望将自己被抓的概率控制在P以下的同时,让自己抢劫到尽可能多的钱。
这道题有意思的地方在于其对应的背包问题里的体积变成了概率,而概率不可以简单的叠加,而是相乘,同时第二重循环也受到了影响,因为概率没办法用循环遍历所有,所以去CSDN上研究了一下别人的思路,发现这个问题被称作变形背包,即交换背包的体积与价值,将思路转化为为在抢劫金额对应的最大概率,然后往下查找,知道出现符合题目的概率情况,然后输出即可。
dp[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=sum;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]*(1-p[i]));
}
}
for(int i=sum;i>=0;i--)
{
if(dp[i]>=(1-p1))
{
cout<<i<<endl;
break;
}
}
(这里使用的是不被抓的最大概率)
数学题
还有一道比较好玩的题目Not Adjacent Matrix,给出一个数n,让构造出nn的方阵,包含了1到nn,其中一个数的上下左右不能是与他邻近的数,不符合输出-1。
一上来没有思路,觉得情况很多,但实际上只用考虑两种特例,一个是1,一个是22,只有22是不可能构成符合条件的方阵,剩下的情况之用让他们先输出奇数在再输出偶数即可(上来考虑输出-1的情况发现只能想到2的时候,没敢写。。)
本周学习总结
这周背包问题的学习对于01背包和完全背包都掌握了基本的模板(改变第二重循环顺序和增加限制条件)题目见得也大都一样,变形背包那道题目反过来后也一样,但是混合背包的问题还没接触到,对于具体的解题方法没有完全掌握,只是觉得与完全背包十分类似,但是二维变一维的思路有了一个新的领悟,不懂的时候就画画图,更加直观(__gcd,一个有用来求多个数的公约数的函数)。