01背包 先空间循环 还是 先物品选取数量循环,空间逆序 还是 空间顺序 10种等价写法 以NOIP 2005 普及组第三题 采药 为例

结论:先空间循环(第二层循环),再物品选取数量循环(第一层循环),空间循环,顺序,逆序随便写,滚动数组,降维数组,随便写。

物品序号物品体积物品价值总体积5
111总价值最大
212
323
424
535
636
1.先空间循环(第二层循环),再物品选取数量循环(第一层循环)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])
dp[i-1][j]表示不选当前序号为i的物品
dp[i-1][j-v[i]]+w[i]表示选当前序号为i的物品
7(124,34)说明:124表示放了序号为1,2,4的物品;34表示放了序号为3,4的物品
选取过程dp[序号][体积]=价值
序号/体积体积0体积1体积2体积3体积4体积5
101(1)1(1)1(1)1(1)1(1)dp[1][n+1]
202(2)3(12)3(12)3(12)3(12)dp[2][n+1]
302(2)3(12,3)5(23)6(123)6(123)dp[3][n+1]
402(2)4(4)6(24)7(124,34)9(235)dp[4][n+1]
502(2)4(4)6(24)7(124,34)9(235,45)dp[5][n+1]
602(2)4(4)6(24,6)8(26)10(46)dp[6][n+1]
m代表物体个数,n代表总体积
从上述数据处理过程,可以但看到,数组dp[m+1][n+1]在空间上可以利用滚动数组dp[2][n+1]处理
空间明显少了许多
序号/体积体积0体积1体积2体积3体积4体积5
101(1)1(1)1(1)1(1)1(1)dp[0][n+1]
202(2)3(12)3(12)3(12)3(12)dp[1][n+1]
302(2)3(12,3)5(23)6(123)6(123)dp[0][n+1]
402(2)4(4)6(24)7(124,34)9(235)dp[1][n+1]
502(2)4(4)6(24)7(124,34)9(235,45)dp[0][n+1]
602(2)4(4)6(24,6)8(26)10(46)dp[1][n+1]
继续处理利用dp[n+1]处理,空间足足少了一维,但是处理是逆向的。
逆向的原因是,空间小的数据对空间大的数据有影响。空间大的数据对空间小的数据无影响
序号/体积体积0体积1体积2体积3体积4体积5
000000第零轮dp[n+1]
101(1)1(1)1(1)1(1)1(1)第一轮dp[n+1]
202(2)3(12)3(12)3(12)3(12)第二轮dp[n+1]
302(2)3(12,3)5(23)6(123)6(123)第三轮dp[n+1]
402(2)4(4)6(24)7(124,34)9(235)第四轮dp[n+1]
502(2)4(4)6(24)7(124,34)9(235,45)第五轮dp[n+1]
602(2)4(4)6(24,6)8(26)10(46)第六轮dp[n+1]
物品序号物品体积物品价值总体积5
111总价值最大
212
323
424
535
636
2.先物品选取数量循环(第二层循环),再空间循环(第一层循环)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])
dp[i-1][j]表示不选当前序号为i的物品
dp[i-1][j-v[i]]+w[i]表示选当前序号为i的物品
体积/序号123456
体积0000000
体积11(1)2(2)2(2)2(2)2(2)2(2)
体积21(1)3(12)3(12,3)4(4)4(4)4(4)
体积31(1)3(12,3)4(13)
体积41(1)
体积51(1)

NOIP 2005 普及组第三题 采药

1.先空间循环(第二层循环),空间序循环,再物品选取数量循环(第一层循环)

AC代码:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[maxn][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(i=1;i<=M;i++)//再物品选取数量循环(第一层循环)
		for(j=1;j<=T;j++){//先空间循环(第二层循环),空间顺序循环 
			f[i][j]=f[i-1][j];//不取当前第i号数据 
			if(j>=t[i])f[i][j]=max(f[i][j],f[i-1][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M][T]);		
	return 0;
}

2.先空间循环(第二层循环),空间序循环,再物品选取数量循环(第一层循环)

 AC代码:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[maxn][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(i=1;i<=M;i++)//再物品选取数量循环(第一层循环)
		for(j=T;j>=1;j--){//先空间循环(第二层循环),空间逆序循环 
			f[i][j]=f[i-1][j];//不取当前第i号数据 
			if(j>=t[i])f[i][j]=max(f[i][j],f[i-1][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M][T]);		
	return 0;
}

3.先空间循环(第二层循环),空间序循环,再物品选取数量循环(第一层循环),滚动数组

  AC代码:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[2][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(i=1;i<=M;i++)//再物品选取数量循环(第一层循环)
		for(j=1;j<=T;j++){//先空间循环(第二层循环),空间顺序循环 
			f[i%2][j]=f[(i-1)%2][j];//不取当前第i号数据 
			if(j>=t[i])f[i%2][j]=max(f[i%2][j],f[(i-1)%2][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M%2][T]);		
	return 0;
}

4.先空间循环(第二层循环),空间序循环,再物品选取数量循环(第一层循环),滚动数组

 AC代码:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[maxn][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(i=1;i<=M;i++)//再物品选取数量循环(第一层循环)
		for(j=T;j>=1;j--){//先空间循环(第二层循环),空间逆序循环 
			f[i%2][j]=f[(i-1)%2][j];//不取当前第i号数据 
			if(j>=t[i])f[i%2][j]=max(f[i%2][j],f[(i-1)%2][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M%2][T]);		
	return 0;
}

 5.先空间循环(第二层循环),空间序循环,再物品选取数量循环(第一层循环),降维数组

 AC代码:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(i=1;i<=M;i++)//再物品选取数量循环(第一层循环)
		for(j=T;j>=1;j--){//先空间循环(第二层循环),空间逆序循环 
			if(j>=t[i])f[j]=max(f[j],f[j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[T]);		
	return 0;
}

 6.先物品选取数量循环(第二层循环),再空间循环(第一层循环),空间序循环,

  AC代码:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[maxn][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(j=1;j<=T;j++)//再空间循环(第一层循环),空间顺序循环 
		for(i=1;i<=M;i++){//先物品选取数量循环(第二层循环)
			f[i][j]=f[i-1][j];//不取当前第i号数据 
			if(j>=t[i])f[i][j]=max(f[i][j],f[i-1][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M][T]);		
	return 0;
}

 (此种情况无效)7.先物品选取数量循环(第二层循环),再空间循环(第一层循环),空间序循环,

代码如下:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[maxn][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(j=T;j>=1;j--)//再空间循环(第一层循环),空间逆序循环 
		for(i=1;i<=M;i++){//先物品选取数量循环(第二层循环)
			f[i][j]=f[i-1][j];//不取当前第i号数据 
			if(j>=t[i])f[i][j]=max(f[i][j],f[i-1][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M][T]);		
	return 0;
}

 (此种情况无效)8.先物品选取数量循环(第二层循环),再空间循环(第一层循环),空间序循环,滚动数组

 代码如下:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[2][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(j=1;j<=T;j++)//再空间循环(第一层循环),空间顺序循环 
		for(i=1;i<=M;i++){//先物品选取数量循环(第二层循环)
			f[i%2][j]=f[(i-1)%2][j];//不取当前第i号数据 
			if(j>=t[i])f[i%2][j]=max(f[i%2][j],f[(i-1)%2][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M%2][T]);		
	return 0;
}

 (此种情况无效)9.先物品选取数量循环(第二层循环),再空间循环(第一层循环),空间序循环,滚动数组

  代码如下:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[2][1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(j=T;j>=1;j--)//再空间循环(第一层循环),空间逆序循环 
		for(i=1;i<=M;i++){//先物品选取数量循环(第二层循环)
			f[i%2][j]=f[(i-1)%2][j];//不取当前第i号数据 
			if(j>=t[i])f[i%2][j]=max(f[i%2][j],f[(i-1)%2][j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[M%2][T]);		
	return 0;
}

 (此种情况无效)10.先物品选取数量循环(第二层循环),再空间循环(第一层循环),空间序循环,降维数组

 

   代码如下:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
int t[maxn],v[maxn],f[1010];
int main(){
	int T,M,i,j;
	scanf("%d%d",&T,&M);
	for(i=1;i<=M;i++)scanf("%d%d",&t[i],&v[i]);
	for(j=T;j>=1;j--)//再空间循环(第一层循环),空间逆序循环 
		for(i=1;i<=M;i++){//先物品选取数量循环(第二层循环)
			if(j>=t[i])f[j]=max(f[j],f[j-t[i]]+v[i]);//尝试取当前第i号数据
		}
	printf("%d\n",f[T]);		
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值