0/1背包入门

本文深入探讨了背包问题的两种经典解决方法:蛮力法求幂集搜索和队列式分支界限法。通过具体实例,详细解释了每种方法的实现过程及代码实现,对比了它们的优劣,为读者提供了理解和解决背包问题的全面视角。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

有n个重量分别为w1 、 w2 、w3、w4···wn的物品,编号1~n,它们的价值为v1 、 v2 、v3、v4···vn。现有一容量为W的背包,求尽可能的把背包装满并使价值最大

下面不妨以n=4 W=6为例

物品编号重量价值
154
234
323
411

蛮力法–求幂集搜索

思路大概是用蛮力法找出n的所有幂集,然后遍历一遍,找到最优解

复杂度为O(2n)

#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<algorithm>
using namespace std;
/**
	求解简单0/1背包问题
	2019-8-12 
*/
vector<vector<int> > ps;
void PSet(int n)  // 求1-n的幂集 
{
	vector<vector<int> > ps1;  // 子幂集 
	vector<vector<int> >::iterator it;
	vector<int> s;
	ps.push_back(s);    // 添加空集合元素
	for(int i=1;i<=n;i++)
	{
		ps1 = ps;
		for(it=ps1.begin();it!=ps1.end();it++)
			(*it).push_back(i);
		for(it=ps1.begin();it!=ps1.end();it++)
			ps.push_back(*it);	
	} 
} 

void Knap(int w[],int v[], int W)
{
	int count = 0;
	int sumw,sumv;
	int maxi,maxsumw=0,maxsumv=0;
	vector<vector<int> >::iterator it;
	vector<int>::iterator sit;
	printf(" 序号   选中物品     总重量    总价值     能否装入\n");
	for(it=ps.begin();it!=ps.end();it++)
	{
		printf(" %d\t",count++);
		sumw=sumv=0;
		printf(" {");
		for(sit=(*it).begin();sit!=(*it).end();sit++)
		{
			printf("%d ",*sit);
			sumw += w[*sit-1];  
			sumv += v[*sit-1];
		} 
		printf("}\t\t%d\t%d ",sumw,sumv);
		if(sumw<=W)
		{
			printf("能\n");
			if(sumv>maxsumv)
			{
				maxsumw = sumw;
				maxsumv = sumv;
				maxi = count-1;	
			} 
		}
		else cout << "否\n";
		//	count++;
	}
	printf("最佳方案为:");
	printf("选中物品");
	printf("{ ");
	for(sit=ps[maxi].begin();sit!=ps[maxi].end();sit++)
		printf("%d ",*sit);
	printf("},");
	printf("总重量:%d,总价值:%d\n",maxsumw,maxsumv); 	 
}
int main() 
{
	int n = 4, W = 6;
	int w[] = {5,3,2,1};
	int v[] = {4,4,3,1};
	PSet(n);
	printf("0/1背包解决方案\n",n);
	Knap(w,v,W);
	return 0;
}

队列式分支界限法

#include<iostream>
#include<queue>
using namespace std;
#define MAXV 20
int maxv = -9999; 
int bestx[20]; // 存放最优解,全局变量
int total = 1;
struct NodeType{
	int num;  // 结点编号 
	int i;    // 当前结点在搜索空间中的层次 
	int w;    // 当前结点的总权重 
	int v;    // 当前结点的总价值 
	int x[MAXV];  // 当前结点包含的解向量 
	double ub; // 上界 
}; 
int n =5,W = 6;
	int w[] = {0,5,3,2,1};
	int v[] = {0,4,4,3,1};
void bound(NodeType &e){  // 计算分支结点e的上界 
	int i=e.i+1;
	int sumw = e.w;
	double sumv = e.v; 
	while((sumw+w[i]<=W)&& i<=n){
		sumw += w[i];
		sumv += v[i];
		i++;
	}
	if(i<=n)
		e.ub = sumv+(W-sumw)*v[i]/w[i];
	else e.ub = sumv; 
} 

void EnQueue(NodeType e,queue<NodeType> &qu)
{
	if(e.i==n)
	{
		if(e.v>maxv)
		{
			maxv = 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.num = 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]<=W)  // 剪枝:检查左孩子结点
		{
			e1.num = 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.num = total++;
		e2.i = e.i+1;
		e2.w = e.w; 
		e2.v = e.v;
		for(j=2;j<=n;j++)
			e2.x[j] = e.x[j];
		e2.x[e2.i]= 0;
		bound(e2);
		if(e2.ub>maxv)
			EnQueue(e2,qu); 
	}
}
int main()
{
	
	bfs();
	printf("分支界限法求解0/1背包问题:\n X=[");
	for(int i=1;i<=n;i++)
		printf("%2d",bestx[i]);
	printf("],装入总价值为%d\n",maxv); 
	
	
	return 0;
}




未完待续~~

参考: 李春葆 .算法设计与分析(第二版)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值