从01背包进化到完全背包

从01背包进化到完全背包

已熟练01背包,是时候进化到完全背包了。

1268:【例9.12】完全背包问题为例

1.原始的仿01背包写完全背包

以下代码是仿01背包编写的代码,却无法AC。

#include <stdio.h>
int f[35][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(k=1;k*w[i]<=M;k++)
			for(j=0;j<=M;j++)
				if(j>=k*w[i])f[i][j]=max(f[i][j],f[i][j-k*w[i]]+k*c[i]),f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*c[i]),f[i][j]=max(f[i][j],f[i-1][j]);
				else f[i][j]=max(f[i][j],f[i-1][j]);
	printf("max=%d\n",f[N][M]);
	return 0;
}

提供上述代码无法AC的数据

Input:
35 8
12 9
2 3
4 9
6 7
14 2
80 5
20 20
10 8
Output:
max=75

请特别关注第6个物体80 5,重量超越了背包承受的重量,造成了f[6][]一系列数据出错。

以下为改进后AC的代码。

#include <stdio.h>
int f[35][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(k=0;k*w[i]<=M;k++)
			for(j=0;j<=M;j++)
				if(j>=k*w[i])f[i][j]=max(f[i][j],f[i][j-k*w[i]]+k*c[i]),f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*c[i]),f[i][j]=max(f[i][j],f[i-1][j]);
	printf("max=%d\n",f[N][M]);
	return 0;
}

请特别关注第6个物体80 5,重量超越了背包承受的重量,上述代码,照样能对f[6][]数据进行完好处理。

问题归结为,装不进物体,造成的错误。

优化上述代码

#include <stdio.h>
int f[35][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(k=0;k*w[i]<=M;k++)
			for(j=0;j<=M;j++)
				if(j>=k*w[i])f[i][j]=max(f[i][j],f[i][j-k*w[i]]+k*c[i]),f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*c[i]);
	printf("max=%d\n",f[N][M]);
	return 0;
}

继续研究数据生成过程,进一步优化上述代码

#include <stdio.h>
int f[35][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(k=0;k*w[i]<=M;k++)
			for(j=0;j<=M;j++)
				if(j>=k*w[i])f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*c[i]);
	printf("max=%d\n",f[N][M]);
	return 0;
}

再接再励,继续阅读代码,发现还有可改进的地方。2019-12-24 21:25

#include <stdio.h>
int f[35][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(k=0;k*w[i]<=M;k++)
			for(j=k*w[i];j<=M;j++)
				f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*c[i]);
	printf("max=%d\n",f[N][M]);
	return 0;
}

能否省去k这重循环呢,仔细分析了数据生成过程,一种物品被选中多次,对结果也是有影响的,故上述代码,已是最优化,不能省去k这重循环,是否真的如此呢,那就看看标准的完全背包写法。2019-12-24 21:30

对比了之前的代码https://blog.csdn.net/mrcrack/article/details/78440134发现上述代码编写确实有独到之处,全网唯一。2019-12-24 21:44

算法精髓,数据模拟。洞悉数据流向。2019-12-24 21:46

瞟了一眼从前编写的代码,发现有回避k这重循环的算法,那么只好在上面代码的基础上静下心来想。

以下为回避k这重循环的算法

#include <stdio.h>
int f[35][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(j=0;j<=M;j++){
			f[i][j]=max(f[i][j],f[i-1][j]);//或者一个同种物体都不放
			if(j>=w[i])f[i][j]=max(f[i][j],f[i][j-w[i]]+c[i]);//贪心,尽可能的多放同种物品
		}
	printf("max=%d\n",f[N][M]);
	return 0;
}

2.原始的仿01背包写完全背包,进行滚动数组优化

//滚动数组优化,
//样例通过,提交AC。2019-12-24 21:26

#include <stdio.h>
int f[2][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(k=0;k*w[i]<=M;k++)
			for(j=k*w[i];j<=M;j++)
				f[i%2][j]=max(f[i%2][j],f[(i-1)%2][j-k*w[i]]+k*c[i]);
	printf("max=%d\n",f[N%2][M]);
	return 0;
}

//滚动数组优化,回避k这重循环的算法.2019-12-25

#include <stdio.h>
int f[2][210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(j=0;j<=M;j++){
			f[i%2][j]=max(f[i%2][j],f[(i-1)%2][j]);//或者一个同种物体都不放
			if(j>=w[i])f[i%2][j]=max(f[i%2][j],f[i%2][j-w[i]]+c[i]);//贪心,尽可能的多放同种物品
		}
	printf("max=%d\n",f[N%2][M]);
	return 0;
}

3.一维数组   2019-12-25

#include <stdio.h>
int f[210],w[210],c[210];
int M,N;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i,j,k;
	scanf("%d%d",&M,&N);
	for(i=1;i<=N;i++)scanf("%d%d",&w[i],&c[i]);
	for(i=1;i<=N;i++)
		for(j=w[i];j<=M;j++)
			f[j]=max(f[j],f[j-w[i]]+c[i]);
	printf("max=%d\n",f[M]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值