0-1背包问题(动态规划求解、公式求解、穷举法、回溯法求解)

#include<stdio.h>
#include<stdlib.h>
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
#define N 100
int a[N]={0};
int temp[N]={0};
int dp[N][N]={0};
int m[N][N]={0};

//0-1背包问题 
// int n=5,c=10;
// int w[]={2,2,6,5,4};
// int v[]={6,3,5,4,6};


//穷举法 时间复杂度T(n)=O(2^n)
//每个物品选或者不选一共2^n种情况
void BagProblem1(int len,int *w,int *v,int capacity)
{
	int i,j,k;
	int maxValue;
	int Value;
	int Weight;
	int flag=0;
	int num=1<<len;
	for(i=0;i<num;i++)
	{
		j=i;
		k=0;
		Value=0;
		Weight=0;
		flag=0;
		while(j)
		{
			if(j&1)
			{
				a[k]=1;
                Value+=v[k];
                Weight+=w[k];
			}
			j>>=1;
			k++;
		}
		if(Weight<=capacity && Value>maxValue)
		{
			maxValue=Value;
			flag=1;
		}
		if(flag==1)
		{
			for(int q=0;q<len;q++)
			{
				temp[q]=a[q];
			}
		}
		for(int q=0;q<len;q++)
		{
			a[q]=0;
		}
	}
	for(int q=0;q<len;q++)
	{
		printf("%d ",temp[q]);
	}
	printf("\n");
	printf("%d\n",maxValue);
}

//公式法 时间复杂度T(n)=O(n^2)
//定义dp[i][j]表示在第1~i个物品(前i个物品)中选择满足背包容量的最大收益
//当第i个物品重量大于背包容量时,dp[i][j]=dp[i-1][j]
//当第i个物品重量小于背包容量时,可选放或者不放
//dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
void BagProblem2(int n,int *w,int *v,int capacity)
{
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<=capacity;j++)
		{
			dp[i][j]=dp[i-1][j];
			if(j>=w[i])
			{
				dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i]);
			}
		}
	}
	printf("%d\n",dp[n-1][capacity]);
}

int main()
{
	int n,c;
	scanf("%d %d",&n,&c);
	int w[n];
	int v[n];
	for(int i=0;i<n;i++)
	{
		scanf("%d",&w[i]);
	}
	for(int i=0;i<n;i++)
	{
		scanf("%d",&v[i]);
	}
	// BagProblem1(n,w,v,c);
	BagProblem2(n,w,v,c);
}

动态规划求解0-1背包问题
表的填写顺序为从下往上从左往右,m(i,j)表示从第i~n个物品中选择满足容量j的最大收益。i=5时,j<4时,装不下第5个物品,效益m(i,j)为0,当j>=4时,装得下,效益m(i,j)为v[5]=6;i=4时,当j<=w[i]=4时,m(i,j)=m(i+1,j),当j>=5时,m(i,j)=max(m(i+1,j),m(i+1,j-w[i])+v[i]),m(4,5)=max(6,0+4)=6

在这里插入图片描述
倒退法输出放入的物品
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
#define N 100
int m[N][N]={0};
int a[N]={0};

//0-1背包问题 

//动态规划 时间复杂度T(n)=O(nc) 空间复杂度S(n)=O(nc)
//m[i][j]表示当前物品是i,从第i~n个物品中选择满足容积的最大效益
//当第i个物品重量大于容积0<=j<wi时,m[i][j]=m[i+1][j]
//当第i个物品重量小于容积j>wi时,m[i][j]=max(m[i+1][j],m[i+1][j-wi]+vi)
//m[n][j]表示把第n个物品放入容积为j的背包中的最大效益
void BagProblem3(int n,int *w,int *v,int capacity)
{
	int jMax=min(w[n]-1,capacity);//避免w[n]>>capacity时数组越界
	//对第n行进行赋值操作
	for(int j=0;j<=jMax;j++)
	{
		m[n][j]=0;
	}
    for(int j=w[n];j<=capacity;j++)
    {
    	m[n][j]=v[n];
    }
    //从第n-1行到第一行进行赋值
    for(int i=n-1;i>=1;i--)
    {
    	int jMax=min(w[i]-1,capacity);
    	for(int j=0;j<=jMax;j++)
    	{
    		m[i][j]=m[i+1][j];
    	}
    	for(int j=w[i];j<=capacity;j++)
    	{
    		m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
    	}
    }
    //返回第一行第capacity列的值为最优值,表示从第1个到第n个物品中选择满足背包容量的最大效益
    printf("%d\n",m[1][capacity]);
}

//使用倒推法求出每个物品是否被选中
//从右上角回退,if(m[i][j]=m[i+1][j])表示没有选中
//if(m[i][j]!=m[i+1][j])表示选中,则看m[i+1][j-w[i]]处的值继续判断
void Traceback(int n,int *w,int *v,int capacity)
{
	for(int i=1;i<n;i++)
	{
		if(m[i][capacity]==m[i+1][capacity])
		{
			a[i]=0;
		}
		else
		{
			a[i]=1;
			capacity=capacity-w[i];
		}
	}
	a[n]=(m[n][capacity]>0?1:0);//最后一行如果值为0表示没放入,非0表示放入
	for(int i=1;i<=n;i++)
	{
		printf("%d ",a[i]);
	}
}


int main()
{
	int n,c;
	scanf("%d %d",&n,&c);
	int w[n+1];
	int v[n+1];
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&w[i]);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&v[i]);
	}
	BagProblem3(n,w,v,c);
	Traceback(n,w,v,c);
}

回溯法求解0-1背包问题

#include<stdio.h>
#include<stdlib.h>
#define N 100
int w[N];
int v[N]; 
int A[N]={0};
int visited[N]={0};
int n,c;
int maxValue=-1;
int value;

// int n=5,c=10;
// int w[]={2,2,6,5,4};
// int v[]={6,3,5,4,6};

void solve(int dep)
{
	if(dep==n)
	{
		if(value>maxValue)
		{
			maxValue=value;
			for(int j=0;j<n;j++)
			{
				A[j]=visited[j];
			}
		}
	}
	else
	{
		//搜索左枝(物品装不进时剪掉左枝) 约束函数
		if(w[dep]<=c)
		{
			c-=w[dep];value+=v[dep];visited[dep]=1;
			solve(dep+1);//继续向下深度搜索
			c+=w[dep];value-=v[dep];visited[dep]=0;//回退
		}
		//搜索右枝 限界函数
		visited[dep]=0;
		solve(dep+1);
	}
}

int main()
{
	scanf("%d %d",&n,&c);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&w[i]);
	} 
	for(int i=0;i<n;i++)
	{
		scanf("%d",&v[i]);
	}
	solve(0);
	printf("%d\n",maxValue);
	for(int i=0;i<n;i++)
	{
		printf("%d ",A[i]);
	}
	printf("\n");
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值