#include<bits/stdc++.h>
using namespace std;
class Knap{
friend int Knapsack(int*,int*,int,int,Knap&);
public:
int c; //背包容量
int n; //物品个数
int* w; //重量信息
int* p; //价值信息
int cw; //当前重量
int cp; //当前价值
int bestp; //当前最优价值
int *x, //当前解
*bestx; //当前最优解
int Bound(int i); //限界函数
void Iterative_Backtrack(); //迭代深度优先遍历
};
int Knap::Bound(int i){ //求解第i层的限界函数(按照背包问题的贪心策略)
int cleft = c - cw;
int b = cp;
while(i<=n&&w[i]<=cleft){
b += p[i];
cleft -= w[i];
i++;
}
if(i<=n) b += cleft*(p[i]/w[i]);
return b;
}
void Knap::Iterative_Backtrack(){
int t = 1;
while(t>0){ //t<=0时表示已经遍历完成,结束循环
while(t<=n && cw+w[t]<=c){ //如果能装下去就继续装,即深度访问其左儿子
x[t] = 1;
cw += w[t];
cp += p[t];
t++;
}
if(t > n){ //如果到达叶子节点,更新最优值和最优解,并输出当前最优解
for(int i=1;i<=n;i++){
bestx[i] = x[i];
}
bestp = cp;
for(int i=1;i<=n;i++){
cout<<x[i]<<" ";
}
cout<<" value "<<cp<<endl;
}
else{ //如果没有到达叶子节点,则访问其右儿子
x[t] = 0;
t++;
}
while(Bound(t+1) <= bestp){ //如果右儿子不满足限界条件则需回溯
t--;
while(t>=1 && !x[t]){ //回溯到右儿子没有被访问过的节点
t--;
}
if(t<=0)
break;
x[t] = 0; //访问最终回溯节点的右儿子
cw -= w[t]; //更新相应值
cp -= p[t];
//再次进入循环判断右儿子是否符合限界函数
}
}
}
class Object{ //物品类
friend int Knapsack(int*,int*,int,int,Knap&);
friend bool cmp(Object,Object);
private:
int id;
double aver;
};
bool cmp(Object a,Object b){
return a.aver>b.aver;
}
int Knapsack(int* w,int* p,int c,int n,Knap& K){ //初始化以及调用重要的函数,因为要访问类内私有成员所以声明为友元函数
int W = 0;
int P = 0;
Object* Q = new Object[n];
for(int i=1;i<=n;i++){
Q[i-1].id = i;
Q[i-1].aver = 1.0*p[i]/w[i];
P += p[i];
W += w[i];
}
if(W<c) return P;
sort(Q,Q+n,cmp); //依照单位重量的价值进行排序
K.p = new int[n+1];
K.w = new int[n+1];
K.x = new int[n+1];
K.bestx = new int[n+1];
for(int i=1;i<=n;i++){ //依照物品的单位重量的价值进行初始化
K.p[i] = p[Q[i-1].id];
K.w[i] = w[Q[i-1].id];
}
K.cp = 0; K.cw = 0; K.c = c; K.n = n; K.bestp = 0;
K.Iterative_Backtrack(); //调用重要遍历函数
delete [] Q; delete [] K.w; delete [] K.p;delete [] K.x;
return K.bestp;
}
void Init(int* &w,int* &p,int& c,int& n){ //输入背包的相关信息
cout<<"input number of object and capacity of bag"<<endl;
cin>>n>>c;
w = new int[n+1];
p = new int[n+1];
cout<<"input weight of objects"<<endl;
for(int i=1;i<=n;i++){
cin>>w[i];
}
cout<<"input value of objects"<<endl;
for(int i=1;i<=n;i++){
cin>>p[i];
}
}
void Print(int bestp,int n,int*& x){
cout<<"max value is"<<endl;
cout<<bestp<<endl;
cout<<"The optimal solution is"<<endl;
for(int i=1;i<=n;i++){
cout<<x[i]<<" ";
}
}
int main(){
int *w=NULL,*p=NULL;
int c,n;
Knap K;
Init(w,p,c,n);
Print(Knapsack(w,p,c,n,K),n,K.bestx);
return 0;
}
//7 150
//35 30 60 50 40 10 25
//10 40 30 50 35 40 30
//170
//开始错误写法
//void Knap::Iterative_Backtrack(){
// int t = 1;
// while(t > 0){
// while(t <= n && cw+w[t] <= c){
// cw += w[t];
// cp += p[t];
// t++;
// }
// if(t>=n){bestp = cp;}
// else{
// cw -= w[t];
// cp -= p[t];
// t--;
// }
// if(cp+Bound(t+1)>bestp){t++;}
// else {t--;}
// }
//
//void Knap::Iterative_Backtrack(){
// int t = 1; //从第一层开始
// while(t > 0){ //当回溯到t = 0时说明已经遍历完成结束循环
// while(t <= n && cw+w[t] <= c){ //如果可以继续装载即可以继续访问左子树,就按深度优先继续访问
// cw += w[t];
// cp += p[t];
// t++;
// }
// if(cp+Bound(t+1) > bestp){t++;} //当装不下t件物品时则停止访问,判断是否可以访问其右子树
// else { //如果右子树也不可访问则需要回溯到上一层
// cw -= w[t];
// cp -= p[t];
// t--;
// }
// if(t > n){bestp = cp;} //如果来到第n+1层则得到一个解,根据约束函数和限界函数可知,此解即为目前最优解,更新
// }
//}
回溯法解决0-1背包问题(迭代)
最新推荐文章于 2022-06-20 18:01:28 发布