改动了 Tyler_Zx 的0-1 背包问题的 4 种解决方法&&算法策略的分支限界,输入程序的详细步骤!
这里先贴上结果图! 详情向下看!
(请忽略我这对的有点不齐)
(过程中有注释,添加的代码都有标注)
下面是完整的源码
/
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100//最多可能的物体数
struct goods //1.物品结构体
{
int sign;//物品的序号
int w;//物品的重量
int p;//物品的价值
} a[N];
bool m(goods a,goods b) //2.比较单位重量价值谁更大
{
return (a.p/a.w)>(b.p/b.w);
}
int max(int a,int b)//3.比较大小
{
return a<b?b:a;
}
int n,C,bestP=0,cp=0,cw=0;//n为物品个数 C为背包容量
int X[N],cx[N];
struct KNAPNODE //4.状态结构体
{
bool s1[N];//当前放入物品
int k;//搜索的深度
int b; //价值上界,背包价值+剩下的最大单位重量价值的物品装满剩余容量的价值和
int w;//物品重量
int p;//物品价值
} ;
/5.堆
struct HEAP //堆元素结构体
{
KNAPNODE *p;;//结点数据
int b;//所指结点的上界
} ;
//交换两个堆元素
void swap(HEAP &a,HEAP &b)
{
HEAP temp=a;
a=b;
b=temp;
}
//堆中元素上移
void mov_up(HEAP H[],int i)
{
bool done=false;
if(i!=1)
{
while(!done && i!=1)
{
if(H[i].b>H[i/2].b)
{
swap(H[i],H[i/2]) ;
}
else{
done =true;
}
i=i/2;
}
}
}
//堆中元素下移
void mov_down(HEAP H[], int n, int i)
{
bool done = false;
if((2*i)<=n)
{
while(!done && ((i = 2*i) <= n))
{
if(i+1 <= n && H[i+1].b > H[i].b)
{
i++;
}
if(H[i/2].b < H[i].b)
{
swap(H[i/2], H[i]);
}
else
{
done = true;
}
}
}
}
//往堆中插入结点
void insert(HEAP H[], HEAP x, int &n)
{
n++;
H[n] = x;
mov_up(H,n);
}
//删除堆中结点
void del(HEAP H[], int &n, int i)
{
HEAP x, y;
x = H[i]; y = H[n];
n --;
if(i <= n)
{
H[i] = y;
if(y.b >= x.b)
{
mov_up(H,i);
}
else
{
mov_down(H, n, i);
}
}
}
//获得堆顶元素并删除
HEAP del_top(HEAP H[], int&n)
{
HEAP x = H[1];
del(H, n, 1);
return x;
}
/堆
//6.计算分支节点的上界,更新这个结点的上界 node.b
void bound( KNAPNODE* node,int M, goods a[], int n)
{
int i = node->k;
float w = node->w;
float p = node->p;
if(node->w > M)
{ //物体重量超过背包载重量
node->b = 0; //上界置为0
}
else
{
多加了一个判断条件这里888888888888888888888888888
if(node->s1[node->k]!=false)
{
while((w+a[i].w <= M)&&(i < n))
{
w += a[i].w; //计算背包已装入载重
p += a[i++].p; //计算背包已装入价值
}
if(i<n)//判断是否是最后一个物品
{
node->b = p + (M - w)*a[i].p/a[i].w;//不是,计算新的上界
}
else
{
node -> b = p;//已经得到最终结果
}
}
多加, node->s1[node->k]==false的时候 8888888888888888888
else{
if(i<n)//判断是否是最后一个物品
{
node->b = p + (M - w)*a[i].p/a[i].w;//不是,计算新的上界
}
else
{
node -> b = p;//已经得到最终结果
}
}
}
}
//7.用分支限界法实现0/1背包问题
int KnapSack(int n,goods a[],int C, int X[])
{
int i, k = 0; //堆中元素个数的计数器初始化为0
int v;
KNAPNODE *xnode, *ynode, *znode; //状态
HEAP x, y, z, *heap; //堆结构体
heap = new HEAP[n*n]; //分配堆的存储空间
for(i = 0; i < n; i++)
{
a[i].sign=i; //记录物体的初始编号
}
sort(a,a+n,m); //对物体按照价值重量比排序
xnode = new KNAPNODE; //建立父亲结点,初始化
for(i = 0; i < n; i++)
{ //初始化结点,表示没有放入背包
xnode->s1[i] = false;
}
xnode->k = xnode->w = xnode->p = 0;//全部初始化为0
/8888888888888888888888888
bound(xnode, C, a, n); //计算结点x的上界
cout<<"w: "<<xnode->w;
cout<<" v:"<<xnode->p;
cout<<" ub:"<<xnode->b;
cout<<" input pt"<<endl;
/888888888888888888888888
while(xnode->k < n) //还有物品需要判断
{
ynode = new KNAPNODE; //建立结点y
*ynode = *xnode; //结点x的数据复制到结点y,也就是全都初始化成0
//也就是y是第一个结点了
/8888888888888888888888888
if(ynode->b!=0)
{
cout<<"w: "<<ynode->w;
cout<<" v:"<<ynode->p;
cout<<" ub:"<<ynode->b;
cout<<" output"<<endl;
}
else
{
xnode->k++;
continue;
}
/8888888888888888888888888
ynode->s1[ynode->k] = true; //装入第k个物体,第一次装入第0个物体
ynode->w += a[ynode->k].w; //背包中物体重量累计
ynode->p += a[ynode->k].p; //背包中物体价值累计
ynode->k++; //搜索深度++
bound(ynode, C, a, n); //计算结点y的上界,更新y的y.b
y.b = ynode->b; //更新pt表中的,也就是优先队列 堆中的数据
y.p = ynode;
/888888888888888888888888888888888
//更新之后,将y结点加入pt表中
if(y.b!=0)
{
cout<<"w:"<< ynode->w;
cout<<" v:"<< ynode->p;
cout<<" ub: "<< y.b;
cout<<" input pt"<<endl;
}
/8888888888888888888888888888888888
insert(heap, y, k); //结点y按上界的值插入堆中
//不装入第k个物体
znode = new KNAPNODE; //建立结点z
*znode = *xnode; //结点x的数据复制到结点z
//测试(忽略):cout<<"zonode w he p: "<<znode->w<<znode->p<<znode->k<<endl;
//测试(忽略):ynode->s1[ynode->k] = true;
znode->s1[znode->k]=false;//不装入物品
znode->k++; //搜索深度++
bound(znode, C, a, n); //计算节点z的上界
//测试(忽略):cout<<"znode.b"<<znode->b;
z.b = znode->b;
z.p = znode;
insert(heap, z, k); //结点z按上界的值插入堆中
//8888888888888888888888888
if(z.b!=0)
{
cout<<"w:"<< znode->w;
cout<<" v:"<< znode->p;
cout<<" ub: "<< z.b;
cout<<" input pt"<<endl;
}
//88888888888888888888888888888888
delete xnode;
x = del_top(heap, k); //获得堆顶元素作为新的父亲结点
xnode = x.p;
}
v = xnode->p;
for(i = 0; i < n; i++)
{ //取装入背包中物体在排序前的序号
if(xnode->s1[i])
{
X[a[i].sign] =1 ;
}
else
{
X[a[i].sign] = 0;
}
}
delete xnode;
delete heap;
return v; //返回背包中物体的价值
}
/*测试以上算法的主函数*/
int main()
{
goods b[N];
printf("物品种数n: ");
scanf("%d",&n); //输入物品种数
printf("背包容量C: ");
scanf("%d",&C); //输入背包容量
for (int i=0;i<n;i++) //输入物品i的重量w及其价值v
{
printf("物品%d的重量w[%d]及其价值v[%d]: ",i+1,i+1,i+1);
scanf("%d%d",&a[i].w,&a[i].p);
b[i]=a[i];
}
int sum=KnapSack(n,a,C,X);//调用分支限界法求0/1背包问题
printf("分支限界法求解0/1背包问题:\nX=[ ");
for(int i=0;i<n;i++)
cout<<X[i]<<" ";//输出所求X[n]矩阵
printf("] 装入总价值[%d]\n",sum);
return 0;
}