背包问题
普通背包:
贪心时间复杂度:O(nlogn)
首先计算每种物品单位重量的价值Vi/Wi,然后,依贪心 选择策略,将尽可能多的单位重量价值最高的物品装入背包。若 将这种物品全部装入背包后,背包内的物品总重量未超过C,则选 择单位重量价值次高的物品并尽可能多地装入背包。依此策略一 直地进行下去,直到背包装满为止
//program 2.3 背包问题
#include<iostream>
#include<algorithm>
using namespace std;
const int M=10005;
struct node{
double w;//每个物品的重量
double v;//每个物品的价值
double p;//性价比
}s[M];
bool cmp(node a,node b){//自定义比较函数
return a.p>b.p;//根据物品的单位价值从大到小排序
}
double solve(int n,double W){
double sum=0.0;//sum表示示装入物品的价值之和
double cleft=W;//背包剩余容量
for(int i=0;i<n;i++){//贪心算法求解
if(s[i].w<=cleft){//如果物品的重量小于等于剩余容量
cleft-=s[i].w;
sum+=s[i].v;
}
else{//如果物品的重量大于剩余容量
sum+=cleft*s[i].p;//部分装入
break;
}
}
return sum;
}
int main(){
int t,n;//t为测试用例个数,n为物品个数
double W;//背包容量
cin>>t;
while(t--){
cin>>n>>W;
for(int i=0;i<n;i++){
cin>>s[i].w>>s[i].v;
s[i].p=s[i].v/s[i].w;//每个宝物单位价值
}
sort(s,s+n,cmp);
cout<<solve(n,W)<<endl;//输出装入宝物的最大价值
}
return 0;
}
分别用动态规划法、回溯法、分支限界法设计 0-1背包问题
动态规划法时间复杂度:O(nc) c是背包容量。
这是因为需要填写一个二维数组来记录子问题的解。
//program 4.8 01背包
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=105;
const int maxw=10005;
int c[maxn][maxw];//c[i][j]表示前i个物品放入容量为j的背包获得的最大价值
int w[maxn],v[maxn];//w[i]表示第i个物品的重量,v[i]表示第i个物品的价值
bool x[maxn]; //x[i]表示第i个物品是否放入背包
int knapsack(int n,int W){
for(int i=1;i<=n;i++){//计算c[i][j]
for(int j=1;j<=W;j++){
if(j<w[i]) //当物品的重量大于背包的容量,则不放此物品
c[i][j]=c[i-1][j];
else //否则比较此物品放与不放哪种情况使得背包内的价值最大
c[i][j]=max(c[i-1][j],c[i-1][j-w[i]]+v[i]);
}
}
return c[n][W];
}
void print(int n,int W){//逆向构造最优解
int j=W;
for(int i=n;i>0;i--){
if(c[i][j]>c[i-1][j]){
x[i]=1;
j-=w[i];
}
else
x[i]=0;
}
cout<<"装入背包的物品序号为:";
for(int i=1;i<=n;i++){
if(x[i])
cout<<i<<" ";
}
cout<<endl;
}
int main(){
int n,W,t;//n表示n个物品,W表示背包的容量,t表示测试用例数
cin>>t;
while(t--){
cin>>n>>W;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++)//初始化第0列为0
c[i][0]=0;
for(int j=1;j<=W;j++)//初始化第0行为0
c[0][j]=0;
cout<<knapsack(n,W)<<endl;
//print(n,W);
}
return 0;
}
/*测试数据
2
5 10
2 6
5 3
4 5
2 4
3 6
4 52
12 13
10 24
22 13
9 24
*/
回溯法O(2的n次方)
//program 5.2 01背包
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=105;
int n; //物品数量
double W; //背包容量
double w[maxn],v[maxn];//w[i]表示第i个物品的重量,v[i]表示第i个物品的价值
double cw,cp,bestp; //当前重量,当前价值,最优值
bool x[maxn]; //x[i]表示第i个物品是否放入背包
bool bestx[maxn]; //最优解
double bound(int i){//计算上界(已装入物品价值+剩余物品(第i~n种物品)的总价值)
double rp=0;
while(i<=n){//依次计算剩余物品的价值
rp+=v[i];
i++;
}
return cp+rp;
}
void backtrack(int t){//回溯法,t为层次
if(t>n){//到达叶子
for(int j=1;j<=n;j++)//记录最优解
bestx[j]=x[j];
bestp=cp;//记录最优值
return ;
}
if(cw+w[t]<=W){//如果满足约束条件,搜索左子树
x[t]=1;
cw+=w[t];
cp+=v[t];
backtrack(t+1);
cw-=w[t];//还原现场
cp-=v[t];
}
if(bound(t+1)>bestp){//如果满足限界条件,搜索右子树
x[t]=0;
backtrack(t+1);
}
}
int knapsack(){
cw=0.0,cp=0.0,bestp=0.0; //当前放入背包的物品重量,价值,最优值
backtrack(1);
cout<<bestp<<endl;
// for(int i=1;i<=n;i++){ //输出最优解
// if(bestx[i]==1)
// cout<<i<<" ";
// }
// cout<<endl;
}
int main(){
int t;//t表示测试用例数
cin>>t;
while(t--){
cin>>n>>W;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
knapsack();
}
return 0;
}
/*测试数据
2
5 10
2 6
5 3
4 5
2 4
3 6
4 52
12 13
10 24
22 13
9 24
*/
分支限界法时间复杂度接近于O(2^n)
//program 6.2 01背包 普通队列bfs AC 831ms
#include<iostream>
#include<queue>
using namespace std;
const int maxn=105;
int n; //物品数量
double W; //背包容量
double w[maxn],v[maxn];//w[i]表示第i个物品的重量,v[i]表示第i个物品的价值
double bestp,sumv; //当前重量,当前价值,最优值,总价值
bool bestx[maxn]; //最优解
struct node{
double cp,rp; //cp当前放入背包的物品价值,rp剩余物品的价值
double rw; //剩余容量
int id; //物品号
node() {}
node(double _cp,double _rp,double _rw,int _id){
cp=_cp;
rp=_rp;
rw=_rw;
id=_id;
}
};
void knapsack_bfs(){
queue<node> q; //创建一个普通队列(先进先出)
q.push(node(0,sumv,W,1)); //根结点入队
while(!q.empty()){ //如果队列不空
node cur,lc,rc;//定义三个结点型变量
cur=q.front();//取出队头元素
q.pop(); //队头元素出队
// cout<<cur.cp<<" "<<cur.rp<<" "<<cur.rw<<" "<<cur.id<<endl;
int t=cur.id;//当前物品序号
if(t>n) continue;
if(cur.cp+cur.rp<bestp) continue;
int cp=cur.cp;
int rp=cur.rp-v[t];
if(w[t]<=cur.rw){ //满足约束条件,可以放入
lc=node(cp+v[t],rp,cur.rw-w[t],t+1);//生成左孩子
if(lc.cp>bestp)//比最优值大更新
bestp=lc.cp;
q.push(lc);//左孩子入队
}
if(cp+rp>bestp){//满足限界条件
rc=node(cp,rp,cur.rw,t+1);//生成右孩子
q.push(rc);//右孩子入队
}
}
}
int main(){
int t;//t表示测试用例数
cin>>t;
while(t--){
cin>>n>>W;
bestp=0.0,sumv=0.0; //sumv为所有物品的总价值
for(int i=1;i<=n;i++){
cin>>w[i]>>v[i];
sumv+=v[i];
}
knapsack_bfs();
cout<<bestp<<endl;
}
return 0;
}
/*测试数据
1
4 10
2 6
5 3
4 5
2 4
*/