问题
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
算法思想
分支限界法与回溯法的求解目标不同。 回溯法的求解目标是找出解空间中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。 由于求解目标不同,导致分支限界法与回溯法对解空间的搜索方式也不相同。 回溯法以深度优先的方式搜索解空间,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间。
回溯法对解空间做深度优先搜索时,有递归回溯和迭代回溯(非递归)两种方法,但一般情况下用递归方法实现回溯法。
常见的两种分支限界法 先进先出(FIFO)队列式:在先进先出的分支限界法中,用队列作为组织活结点表的数据结构,并按照队列先进先出的原则选择结点作为扩展结点。 优先队列(PQ):用优先队列作为组织活结点表的数据结构。
回溯代码
#include<stdio.h>
int n,c,bestp;
int p[10000],w[10000],x[10000],bestx[10000];
void Backtrack(int i,int cp,int cw)
{
int j;
if(i>n)
{
if(cp>bestp)
{
bestp=cp;
for(i=0;i<=n;i++) bestx[i]=x[i];
}
}
else
for(j=0;j<=1;j++)
{
x[i]=j;
if(cw+x[i]*w[i]<=c)
{
cw+=w[i]*x[i];
cp+=p[i]*x[i];
Backtrack(i+1,cp,cw);
cw-=w[i]*x[i];
cp-=p[i]*x[i];
}
}
}
int main()
{
int i;
bestp=0;
printf("请输入背包最大容量:\n");
scanf("%d",&c);
printf("请输入物品个数:\n");
scanf("%d",&n);
printf("请依次输入物品的重量:\n");
for(i=1;i<=n;i++)
scanf("%d",&w[i]);
printf("请依次输入物品的价值:\n");
for(i=1;i<=n;i++)
scanf("%d",&p[i]);
Backtrack(1,0,0);
printf("最大价值为:\n");
printf("%d\n",bestp);
printf("被选中的物品依次是(0表示未选中,1表示选中)\n");
for(i=1;i<=n;i++)
printf("%d ",bestx[i]);
printf("\n");
return 0;
}
回溯结果
分支限界代码
#include<iostream>
#include<queue>
using namespace std;
const int maxn=99;
int n,c;
int w[maxn];
int v[maxn];
int bestv=0;
int bestx[maxn];
int total=1;
struct nodetype
{
int no;
int i;
int w;
int v;
int x[maxn];
double ub;
};
void input()
{
cout<<"请输入物品的个数:"<<endl;
cin>>n;
cout<<"请输入每个物品的重量及价值(如5 4):"<<endl;
for(int i = 1; i <= n; i++)
{
cin>>w[i]>>v[i];
}
cout<<"请输入背包的容量:"<<endl;
cin>>c;
}
void bound(nodetype &e)
{
int i=e.i+1;
int sumw=e.w;
double sumv=e.v;
while((sumw+w[i]<=c)&&i<=n)
{
sumw+=w[i];
sumv+=v[i];
i++;
}
if(i<=n)
e.ub=sumv+(c-sumw)*v[i]/w[i];
else e.ub=sumv;
}
void enqueue(nodetype e,queue<nodetype> &qu)
{
if(e.i==n)
{
if(e.v>bestv)
{
bestv=e.v;
for(int j=1;j<=n;j++)
bestx[j]=e.x[j];
}
}
else qu.push(e);
}
void bfs()
{
int j;
nodetype e,e1,e2;
queue<nodetype> qu;
e.i=0;
e.w=0;
e.v=0;
e.no=total++;
for(j=1;j<=n;j++)
e.x[j]=0;
bound(e);
qu.push(e);
while(!qu.empty())
{
e=qu.front();qu.pop();
if(e.w+w[e.i+1]<=c)
{
e1.no=total++;
e1.i=e.i+1;
e1.w=e.w+w[e1.i];
e1.v=e.v+v[e1.i];
for(j=1;j<=n;j++)
e1.x[j]=e.x[j];
e1.x[e1.i]=1;
bound(e1);
enqueue(e1,qu);
}
e2.no=total++;
e2.i=e.i+1;
e2.w=e.w;
e2.v=e.v;
for(j=1;j<=n;j++)
e2.x[j]=e.x[j];
e2.x[e2.i]=0;
bound(e2);
if(e2.ub>bestv)
enqueue(e2,qu);
}
}
void output()
{
cout<<"最优值是:"<<bestv<<endl;
cout<<"(";
for(int i=1;i<=n;i++)
cout<<bestx[i]<<" ";
cout<<")";
}
int main()
{
input();
bfs();
output();
return 0;
}
分支限界结果
|