01背包回溯法(记忆化搜索)

0/1背包问题(回溯)
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2866 Accepted Submission(s): 751
Description
给定n个物品和一个背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应该如何选择装入背包的物品,使得装入背包中物品的总价值最大?
Input
输入的第一行为测试样例的个数T,接下来有T个测试样例。
每个测试样例的第一行是物品个数n(1 ≤ n ≤ 100)和背包容量C(C ≤ 1000),接下来n行,每行两个正整数 wi和 vi( wi ≤ 1000, vi ≤ 1000 ),分别表示第i件物品的重量 wi及其价值 vi。
Output
对应每个测试样例输出一行,只有一个整数,表示总价值的最大值。
Sample Input
2
1 2
1 1
2 3
2 3
2 4
Sample Output
1
4
所谓回溯法就是让你写递归
本人觉得递归不就是爆搜吗?所以就写了以下代码:

/*int maxv,weight,value;//最大价值 临时重量价值
void dfs(int step)
{
	if(step==n+1)
	{
		maxv=max(value,maxv);
		return;
	}
	else
	    for(int j=0;j<=1;j++){//01背包 0 表示不取 1 表示取
			if(j==1&&weight+w[step]<=c) 
			{
				weight+=w[step];
				value+=v[step]; 
			}//取
			dfs(step+1);
			if(j==1)//取消尝试 
			{
				weight-=w[step];
				value-=v[step]; 
			}
	    }
	return;
}

不要说了,没有任何剪枝,直接超时。
所以我就在想有没有比较好的剪枝,我尝试了,我是想不出来【捂脸】(hh能力有限),不过我觉得单纯的剪枝估计优化不了多少时间。
那就,既要递归,那么就只有记忆化搜索了:
给个大佬链接:洛谷-这题题解第二个作者: interestingLSY
大佬写的太好了,我就直接复制粘贴了:
总结一下记忆化搜索是啥:
不依赖任何 外部变量
答案以返回值的形式存在, 而不能以参数的形式存在(就是不能将 dfs 定义成 dfs(pos ,tleft , nowans )dfs(pos,tleft,nowans), 这里面的 nowans 不符合要求).
对于相同一组参数, dfs 返回值总是相同的

那么 如何写记忆化搜索
方法I(由动态规划开始思考):
1把这道题的dp状态和方程写出来
2根据他们写出dfs函数
3添加记忆化数组

方法II(由暴搜开始思考):
1写出这道题的暴搜程序(最好是dfs)
2将这个dfs改成"无需外部变量"的dfs
3添加记忆化数组
举例: 本文最开始介绍"什么是记忆化搜索"时举的"采药"那题的例子,就是典型的方法II
先把不依赖外部变量的递归写出来(答案以返回值的形式存在, 而不能以参数的形式存在)

    int dfs(int pos,int weight)
{
    if(pos==n+1)
    return 0;
    int s1,s0;
    s0=dfs(pos+1,weight)
    if(weight>=w[i])
    s1=dfs(pos+1,weight-w[i])+v[i];
    return max(s1,s0);
}

进而用记忆化数组记录搜索结果

int dp[105][1005];
int dfs(int pos,int weight)
{
	if(dp[pos][weight]!=-1)
	return dp[pos][weight];
	if(pos==n+1)
	return dp[pos][weight]=0;
	//dp->f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);//max( 不挑选,挑选 );
	int s0,s1=-99999999;//一定要初始化 
	s0=dfs(pos+1,weight);
	if(weight>=w[pos])//因为s1可能取不到,所以要先初始化 
    s1=dfs(pos+1,weight-w[pos])+v[pos];
	return  dp[pos][weight]=max(s1,s0);//挑 不挑
}

主函数:

#include<bits/stdc++.h>
using namespace std;
int c,n;
int w[105],v[105];
int main(void)
{
	int t,i;
	scanf("%d",&t);
	while(t--)
	{
		//maxv=weight=value=0;
		scanf("%d %d",&n,&c);
		for(i=1;i<=n;i++)
		scanf("%d %d",&w[i],&v[i]);
		/*dfs(1);
		printf("%d\n",maxv);*/
		memset(dp,-1,sizeof(dp));
		printf("%d\n",dfs(1,c)); 
	}
	return 0;
}

具体总结还是看大佬的吧,写的非常全面
ps:学过数位DP的同学应该能想到那个也是用来记忆化搜索
另外想了解01背包的DP写法请点这里:https://blog.csdn.net/qq_43791377/article/details/87998750
(当入门都行)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值