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
(当入门都行)