多重背包问题解法简单分析(POJ 1276为例)
来源:互联网 发布:中国网络直播平台排名 编辑:程序博客网 时间:2024/06/08 02:38
先简单介绍几种背包问题:
01背包(ZeroOnePack): 有N件物品和一个容量为V的背包, 每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
完全背包(CompletePack): 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
多重背包(MultiplePack): 有N种物品和一个容量为V的背包,第i种物品最多有n[i]件可用。每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
参考:http://www.wutianqi.com/?p=539
对于多重背包问题,可以用动态规划的思路来求解,最优解的递推公式为:
减包方法(个人随便取的,不做参考):
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i],k*c[i]<=v}
表示花费为v且且装前i种物品的收益
思路是第i种物品装k个,剩下的花费v-k*c[i]都用前i-1种来装
添包方法:
已经知道了f[i-1][v],我现在要添加第i种物品数量为k
f[i][v+k*c[i]]=max{f[i][v+c*[i]],f[i-1][v]+k*w[i]}
POJ 1276 解法的启示优化方法分析:
该题为非典型的多重背包问题,其费用和价值相等。
解法1:直接利用动态规划求解
#include <iostream>
#include <cstdio>
using namespace std;
#define MAX 100001
#define N 11
int cash,n,c[N],d[N];
bool w[N][MAX];
int solve(){
int i,j,k;
memset(w,false,sizeof(w));
w[0][0] = true;
for(i=1;i<=n;i++){
for(j=cash;j>=0;j--){
for(k=0;k<=c[i]&&k*d[i]<=j;k++)
{
if(w[i][j])break;
w[i][j] = w[i-1][j-k*d[i]];
}
}
if(w[i][cash]){return cash;}
}
for(j=cash;j>=0;j--)
if(w[n][j])
return j;
}
int main(){
int i;
while(scanf("%d%d",&cash,&n)!=EOF){
for(i=1;i<=n;i++)
scanf("%d%d",&c[i],&d[i]);
printf("%d\n",solve());
}
return 0;
}
结果分析:该方法直接按照动态规划的思路进行,采用减包操作,由于收益和花费相等,直接用bool来标识是否可以花费出这个量。对增加的一种新物品,给定花费x,剔除该新物品的整数倍的花费,看剩余的花费前i-1种物品是否能够达到。没有做任何优化,结果是超时。
解法2:对能得到的花费进行添包操作(优化)
#include <iostream>
#include <cstdio>
using namespace std;
#define MAX 100001
#define N 11
int cash,n,c[N],d[N];
bool w[N][MAX];
int solve(){
int i,j,k;
memset(w,false,sizeof(w));
w[0][0] = true;
int cmax = 0;
for(i=1;i<=n;i++){
for(j=cmax;j>=0;j--){ //添加前i-1种物品所能用到的最多花费
if(!w[i-1][j])continue; //不能得到这个花费的就不用计算
for(k=0;k<=c[i]&&k*d[i]+j<=cash;k++)
{
w[i][j+k*d[i]] = true;
if(j+k*d[i] > cmax)cmax=j+k*d[i];
}
}
if(cmax==cash){return cash;}
}
return cmax;
}
int main(){
int i;
while(scanf("%d%d",&cash,&n)!=EOF){
for(i=1;i<=n;i++)
scanf("%d%d",&c[i],&d[i]);
printf("%d\n",solve());
}
return 0;
}
结果分析:采用添包方法,优化两点,一是记录了前i-1种物品的最大可能花费,二是只对能花费的数进行继续添包以得到新的花费。经过优化之后运行时间约400多ms。但是显然该算法的时间复杂度还是很高,为O(nmk),n为物品种类数,m为容积,k为每类物品的数量。
解法3:叠加逐个添加(优化)
#include <iostream>
#include <cstdio>
using namespace std;
#define MAX 100001
#define N 11
int use[MAX],cash,n,c[N],d[N];
bool w[MAX];
int solve(){
if(cash == 0 || n == 0)return 0;
int i,j,k;
memset(w,false,sizeof(w));
w[0] = true;
for(i=1;i<=n;i++){
memset(use,0,sizeof(use));
for(j=d[i];j<=cash;j++){
if(!w[j] && w[j-d[i]] && use[j-d[i]]<c[i]){ //第i种物品添加得到新花费,收益大,不超量
use[j]=use[j-d[i]]+1; //记录新添加
w[j]=true;
}
}
}
for(j=cash;j>=0;j--)if(w[j])return j;
}
结果分析:运行时间减少到16ms,显然是时间复杂度降到O(nm)了。这种解法和前面两种解法的主要不同在于前面解法思路清晰,那就是我在添加第i种物品之前是一定没有第i种物品的,所以要考虑第i种物品添加的量。而本解法则不需要考虑这个,因为我添加第i种物品之前可能就已经有了第i种物品。那么我该怎么来防止第i种物品超过实际存有量呢,所以在开始处理第i种物品时用一个数组来计算当花费x时用去的最小的i种物品的量。
- 多重背包问题解法简单分析(POJ 1276为例)
- poj 1276 多重背包+二进制解法
- 多重背包问题(悲剧解法)
- POJ 1276 多重背包问题
- poj 1276 多重背包问题
- poj 1276 多重背包问题
- POJ-1276 多重背包问题
- nyoj106_背包问题(贪心or多重背包解法)
- POJ 1742 Coins (多重背包的两种解法)
- POJ 1276(多重背包)
- poj 1276(多重背包)
- POJ-1276(多重背包)
- POJ 1276(多重背包)
- POJ 题目1276 Cash Machine(多重背包问题)
- POJ 1276 Cash Machine(多重背包问题)
- POJ-1276 Cash Machine(多重背包问题)
- POJ 1276 Cash Machine 多重背包问题
- poj 1276 多重背包..
- oracle数据库的使用及安装
- 一些简单的Linux命令
- JUnit中标注Annotation介绍
- 利用Firebug和XPath寻找locator
- Selenium 1.0使用问题集锦
- 多重背包问题解法简单分析(POJ 1276为例)
- 塔防游戏
- hdfs文件写入过程流程分析
- HDU 2065
- mac80211
- Canvas中svae()与restore()的使用
- Java接口中属性和方法的默认修饰符
- SQL Server 问题汇总
- poj 1005 [简单计算]