栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

01背包问题——记忆化搜索的妙用

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

01背包问题——记忆化搜索的妙用

目录

题目最基本的想法

思想

引申

解决方案解决方案代码 其他解法

==memset原理==

题目

最基本的想法
#include

using namespace std;

int n,W;
const int MAX_SIZE=100;
int w[MAX_SIZE+1],v[MAX_SIZE+1];

void solve();
int rec(int,int);


int main(){
    cin >> n >> W;
    for(int i=0;i> w[i] >> v[i];
    }
    solve();
}

void solve(){
    cout << rec(0,W);
}

int rec(int i,int j){//i表示从第几件物品开始取,j表示能取的容量还有多少
    int res;//每一层都会有一个叫做res的变量,用于记录当前层数下的背包价值
    if(i==n){//物品的编号是从0~n-1的,这里==n表示已经取到头了
        res=0;
    }else if(j 
思想 

这种解法的思想就是
要知道在容量为W,可选物品为0~n-1的情况最大价值是多少
我们就去寻找

    在容量为W,可选物品为1~n-1的情况最大价值是多少(不拿第1件物品之后)在容量为W-j,可选物品为1~n-1的情况最大价值是多少(拿第一件物品之后)
    这样递归的结构一目了然
    然后我们再把当前层的最优解存在res里,层层返回,就得到了最终的最优解
    输出即可
这里我们还可以发现,递归都是从复杂问题入手,逐渐变成简单问题,且具有相同子结构~
引申

可能已经有读者已经发现了,上面的形式与二叉树相似,因此我们可以说它的复杂度为n2 ,因此,当n比较大的时候,翻车是必然的。除此之外还可能会出现重复计算的问题。

解决方案

因此,我们可以用一个数组把内容记录下来,这样用到的时候直接调用即可。

解决方案代码
#include

using namespace std;

int n,W;
const int MAX_SIZE=100;
int w[MAX_SIZE+1],v[MAX_SIZE+1];
int record[110][1010];// todo 用于记录每次res的值

void solve();
int rec(int,int);


int main(){
    cin >> n >> W;
    for(int i=0;i> w[i] >> v[i];
    }
    solve();
    system("pause");
    return 0;
}

void solve(){
    cout << rec(0,W);
}

int rec(int i,int j){
    if(record[i][j]==0){
        int res;
        if(i==n){
            res=0;
        }else if(j 

超时的问题迎刃而解~

其他解法

我们可不可以不利用递归写呢?答案是可以
复杂度也是n*W,其实和上文思路是一样的,但是这里我们用两个for循环

假设当前背包容量为V,物品的价值为val,体积为wgt,我们要考虑的就是这个物品要不要拿?那么怎么进行比较呢,我们可以用背包容量为V-wgt时的价值+val(相当于拿这个物体),与现在容量为V时的价值进行比较(也就相当于不拿这个物品),如果前者的价值更高,那么就选择拿这个物体,如果不拿这个物体时的价值高,那么就不拿这个物体。
这个想法有点绕,笔者拙笨,不能清楚的讲述出来,更多内容可参见大佬的背包九讲。

#include
#include
using namespace std;

int n,W;//n表示物品数量,W表示背包容量

struct thing
{
    int _w,_v;
    thing(int w,int v){
        this->_w=w;
        this->_v=v;
    }
    thing(){};
};


int main(){
	cin >> n >> W;
    thing T[110];
	for(int i=0;i> T[i]._w >> T[i]._v;
	}
    int w[1010]={0};
    //首先肯定是双重循环控制,外层循环控制是哪个物品
    //内层循环控制背包当前容量
    for(int i=0;i=0;j--){//这里的容量就是从W开始计算的,并不用考虑W-1
            int v=j-T[i]._w;
            if(v>=0){
                w[j]=max(w[j],w[v]+T[i]._v);
            }
        }
    }
    //迭代完成,查看最终结果
    cout << w[W] << endl;
    return 0;
}

// todo 我们的思路是



顺带一提,使用包含在cstring中的memset(array,val,sizeof(array))函数可以快速初始化高维数组的值为指定的值(val)。包含的头文件不能写成string,必须是cstring。
注意!!!不能将数组的值设为1等其他值

可以设置成0或-1

memset原理

memset修改的是内存,一次直接修改一个字节(8位),而我们的数组是32位int型的,如果设置成1的话用2进制来表示是00000001,一共表示4次,得到的值换算成10进制就是

那么为什么可以刷成0或-1呢?
0自然不必解释00000000
-1就涉及到补码的知识了
在计算机组成原理中我们学过原码反码补码
我们知道-1的原码是符号位加上真值的绝对值,因此-1的原码是10000001,将-1的原码转换为反码,方法是符号位保持不变,其余按位取反,因此-1的反码是11111110,最后将反码转换为补码,末位+1,符号位保持不变,得到-1的补码11111111,因此我们可以利用memset快速将数组赋值成-1~

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/738520.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号