0-1背包问题 --分支限界法--完整代码---(输出每一个结点状态)

改动了 Tyler_Zx0-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;

}
	 
	 



  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用先进先出队列式分支限界法求解0-1背包问题的完整C代码: ``` #include<stdio.h> #include<stdlib.h> #define MAX_SIZE 100 //物品结构体 typedef struct _item{ int weight; //重量 int value; //价值 int bound; //价值上界 }Item; //结点结构体 typedef struct _node{ int level; //决策树层数 int profit; //当前已获得的价值 int weight; //当前已占用的重量 int bound; //价值上界 int select[MAX_SIZE]; //选择情况 }Node; //队列结构体 typedef struct _queue{ Node elem[MAX_SIZE]; //队列元素 int front; //队头指针 int rear; //队尾指针 }Queue; //初始化队列 void initQueue(Queue *q){ q->front = q->rear = 0; } //判断队列是否为空 int isEmpty(Queue *q){ if(q->front == q->rear) return 1; else return 0; } //进队列 void enqueue(Queue *q, Node n){ if((q->rear+1)%MAX_SIZE == q->front){ printf("Queue is full!\n"); exit(1); } q->elem[q->rear] = n; q->rear = (q->rear+1)%MAX_SIZE; } //出队列 Node dequeue(Queue *q){ if(isEmpty(q)){ printf("Queue is empty!\n"); exit(1); } Node n = q->elem[q->front]; q->front = (q->front+1)%MAX_SIZE; return n; } //计算结点的价值上界 int bound(Node n, int nItems, Item items[]){ int j, k; int totalWeight; int boundValue; //剩余物品全部装入背包 if(n.weight >= items[n.level].weight){ boundValue = n.profit; totalWeight = n.weight; for(j=n.level+1; j<nItems; j++){ if(totalWeight+items[j].weight <= MAX_SIZE){ totalWeight += items[j].weight; boundValue += items[j].value; }else{ k = MAX_SIZE-totalWeight; boundValue += (int)(k*(items[j].value/items[j].weight)); break; } } } //剩余物品不能全部装入背包 else{ boundValue = n.profit+(int)((MAX_SIZE-n.weight)*(items[n.level].value/items[n.level].weight)); totalWeight = MAX_SIZE; } return boundValue; } //先进先出队列式分支限界法 int knapsack(int nItems, Item items[], int capacity, int *solution){ Queue q; Node u, v; int i; initQueue(&q); //初始化根结点 u.level = -1; u.profit = 0; u.weight = 0; //计算根结点的价值上界 u.bound = bound(u, nItems, items); enqueue(&q, u); int maxProfit = 0; while(!isEmpty(&q)){ u = dequeue(&q); //如果结点的价值上界小于当前最优解,则剪枝 if(u.bound <= maxProfit) continue; //扩展结点 if(u.level < nItems-1){ //不选当前物品 v.level = u.level+1; v.weight = u.weight; v.profit = u.profit; v.bound = bound(v, nItems, items); for(i=0; i<=u.level; i++){ v.select[i] = u.select[i]; } v.select[v.level] = 0; enqueue(&q, v); //选当前物品 v.level = u.level+1; v.weight = u.weight+items[v.level].weight; v.profit = u.profit+items[v.level].value; v.bound = bound(v, nItems, items); for(i=0; i<=u.level; i++){ v.select[i] = u.select[i]; } v.select[v.level] = 1; //更新当前最优解 if(v.profit > maxProfit){ maxProfit = v.profit; for(i=0; i<nItems; i++){ solution[i] = v.select[i]; } } //如果结点的价值上界大于当前最优解,则加入队列 if(v.bound > maxProfit){ enqueue(&q, v); } } } return maxProfit; } int main(){ int nItems = 5; Item items[5] = {{2, 12, 0}, {1, 10, 0}, {3, 20, 0}, {2, 15, 0}, {5, 25, 0}}; int capacity = 8; int solution[5] = {0}; int maxProfit = knapsack(nItems, items, capacity, solution); printf("Total profit: %d\n", maxProfit); printf("Solution: "); for(int i=0; i<nItems; i++){ printf("%d ", solution[i]); } printf("\n"); return 0; } ``` 其中,Item结构体存储物品的重量、价值和价值上界;Node结构体存储结点的决策树层数、当前已获得的价值、当前已占用的重量、价值上界和选择情况;Queue结构体为先进先出队列。在主函数中,定义了5个物品,背包容量为8,使用solution数组存储选中的物品,最终输出了最大价值和选择情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值