完全背包问题是指
有N种物品和一个容量为V的背包,每一种物品都是有无限的个数。也就是从每种物品角度分析,与其相关的策略不是选不选的问题了 已经转换为选多少个的问题了
其实说到这里你难道不觉得有点像DAG图的银币问题吗 只是那里的权值是1 这里的权值是W
(其实我一开始在想一个问题 DAG图不是确定起点以及终点的求最小或者最长路径的嘛 这里只是确定了起点 而且终点只是一个范围值 因为有可能是放不满的)
但是再想想DAG图的硬币问题也是放不满的,因为你不能保证每一都能刚刚好筹到N 所以这里完全背包问题和DAG图的硬币问题是一样的,所以当你求最大值的时候就要初始化数组为负无穷但是数组0的地方为0
这样就可以保证上一个是存在的
那么按照DAG图的硬币问题 就是可以处理每一个状态 (每一个状态都是选择每一个背包来计算 然后把个数转换为重量)
for(int i=1;i<=S;i++)
for(int j=1;j<=n;j++)
if(i>=V[j])
{
min1[i]=Min(min1[i],min1[i-V[j]]+W);
max1[i]=Max(max1[i],max1[i-V[j]]+W);
}
时间复杂都是NS
现在我来说说完全背包问题没有优化的思路吧,其实完全背包问题也是有点像01背包问题那样,但是因为背包的数目是无限的 那么转移方程是f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<= v} 每一状态都要处理O(v/c[i])次 那么总的处理次数是超过O(VN)
这里有一个小小的思路吧 假如有两个背包 背包体积第一个比第二个小 并且第一个背包的价值比第二个大 那么就要舍弃第二个了 但是这样还是改变不了最不利的情况
现在说一下背包问题的二进制优化问题
其实01背包问题是最简单的背包问题 那么我们就在想能不能把完全背包问题以及多重背包问题转换为01背包问题,其实这里之间的转换就是差了个二进制优化的操作,就是把一个背包为一个每次能选择一次的新背包集合
下面先说说关于多重背包的二进制优化(个人觉得这个比较容易理解)
假设有一种大米只有M袋 例如M为100 利用二进制来分解
for(int j=1;j<=a[i].num;j<<=1){
//左移一位等价于乘以 2
weigh[coun]=j*a[i].weight;
size[coun++]=j*a[i].value;
a[i].num-=j;
}
当a[i].num不为0的时候就
weigh[coun]=a[i].num*a[i].weight;
size[coun++]=a[i].num*a[i].value;
100=1+2+4+8+16+32+37
本来正常的思路是把100弄成100个1 即100个只能使用一次的物品 但是现在把100弄成1+2+4+8+16+32+37 7个数可以组合成100以内的任意一个数 所以可以这样拆分 把每一个背包都拆成一个集合之后 在进行01背包的处理
for(int i=0;i<n;i++){
scanf("%d%d%d",&a[i].value,&a[i].weight,&a[i].num);
//---------------------下面在输入后进行分解------------------------------//
for(int j=1;j<=a[i].num;j<<=1){
//左移一位等价于乘以 2
weigh[coun]=j*a[i].weight;
size[coun++]=j*a[i].value;
a[i].num-=j;
}
//-----------------------处理剩余的部分----------------------------------//
if(a[i].num>0){
weigh[coun]=a[i].num*a[i].weight;
size[coun++]=a[i].num*a[i].value;
}
}
//--------------------0 1 背包问题求解--------------------------------//
memset(dp,0,sizeof(dp));
for(int i=0;i<coun;i++)
for(int j=cash;j>=size[i];j--)
dp[j]=max(dp[j],dp[j-size[i]]+weigh[i]);
好了现在来说一下完全背包问题的二进制优化问题吧 因为完全背包问题每一种背包都是没有数目的要求的。
所以你只需要你所弄的背包集合的空间集合是小于总集合的
当跳出第一循环的时候 应该补齐剩下的(剩下的空间除以自己空间剩余的个数)
其实还是有一种更加方便的思路的 就是用一个数组记录每一种背包最多可以存在的数目 然后就可以把它视为
限制的个数 然后就可以像多重背包那样处理
有N种物品和一个容量为V的背包,每一种物品都是有无限的个数。也就是从每种物品角度分析,与其相关的策略不是选不选的问题了 已经转换为选多少个的问题了
其实说到这里你难道不觉得有点像DAG图的银币问题吗 只是那里的权值是1 这里的权值是W
(其实我一开始在想一个问题 DAG图不是确定起点以及终点的求最小或者最长路径的嘛 这里只是确定了起点 而且终点只是一个范围值 因为有可能是放不满的)
但是再想想DAG图的硬币问题也是放不满的,因为你不能保证每一都能刚刚好筹到N 所以这里完全背包问题和DAG图的硬币问题是一样的,所以当你求最大值的时候就要初始化数组为负无穷但是数组0的地方为0
这样就可以保证上一个是存在的
那么按照DAG图的硬币问题 就是可以处理每一个状态 (每一个状态都是选择每一个背包来计算 然后把个数转换为重量)
for(int i=1;i<=S;i++)
for(int j=1;j<=n;j++)
if(i>=V[j])
{
min1[i]=Min(min1[i],min1[i-V[j]]+W);
max1[i]=Max(max1[i],max1[i-V[j]]+W);
}
时间复杂都是NS
现在我来说说完全背包问题没有优化的思路吧,其实完全背包问题也是有点像01背包问题那样,但是因为背包的数目是无限的 那么转移方程是f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<= v} 每一状态都要处理O(v/c[i])次 那么总的处理次数是超过O(VN)
这里有一个小小的思路吧 假如有两个背包 背包体积第一个比第二个小 并且第一个背包的价值比第二个大 那么就要舍弃第二个了 但是这样还是改变不了最不利的情况
现在说一下背包问题的二进制优化问题
其实01背包问题是最简单的背包问题 那么我们就在想能不能把完全背包问题以及多重背包问题转换为01背包问题,其实这里之间的转换就是差了个二进制优化的操作,就是把一个背包为一个每次能选择一次的新背包集合
下面先说说关于多重背包的二进制优化(个人觉得这个比较容易理解)
假设有一种大米只有M袋 例如M为100 利用二进制来分解
for(int j=1;j<=a[i].num;j<<=1){
//左移一位等价于乘以 2
weigh[coun]=j*a[i].weight;
size[coun++]=j*a[i].value;
a[i].num-=j;
}
当a[i].num不为0的时候就
weigh[coun]=a[i].num*a[i].weight;
size[coun++]=a[i].num*a[i].value;
100=1+2+4+8+16+32+37
本来正常的思路是把100弄成100个1 即100个只能使用一次的物品 但是现在把100弄成1+2+4+8+16+32+37 7个数可以组合成100以内的任意一个数 所以可以这样拆分 把每一个背包都拆成一个集合之后 在进行01背包的处理
for(int i=0;i<n;i++){
scanf("%d%d%d",&a[i].value,&a[i].weight,&a[i].num);
//---------------------下面在输入后进行分解------------------------------//
for(int j=1;j<=a[i].num;j<<=1){
//左移一位等价于乘以 2
weigh[coun]=j*a[i].weight;
size[coun++]=j*a[i].value;
a[i].num-=j;
}
//-----------------------处理剩余的部分----------------------------------//
if(a[i].num>0){
weigh[coun]=a[i].num*a[i].weight;
size[coun++]=a[i].num*a[i].value;
}
}
//--------------------0 1 背包问题求解--------------------------------//
memset(dp,0,sizeof(dp));
for(int i=0;i<coun;i++)
for(int j=cash;j>=size[i];j--)
dp[j]=max(dp[j],dp[j-size[i]]+weigh[i]);
好了现在来说一下完全背包问题的二进制优化问题吧 因为完全背包问题每一种背包都是没有数目的要求的。
所以你只需要你所弄的背包集合的空间集合是小于总集合的
当跳出第一循环的时候 应该补齐剩下的(剩下的空间除以自己空间剩余的个数)
其实还是有一种更加方便的思路的 就是用一个数组记录每一种背包最多可以存在的数目 然后就可以把它视为
限制的个数 然后就可以像多重背包那样处理