背包问题(动态规划)

01背包问题

有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不插过W的物品,求所有挑选方案中价值总和的最大值

限制条件

1<=n<=100

1<=wi,vi<=100

1<=W<=10000

常规通法DFS

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 1<<23
#define mod 1e9+7
typedef long long LL;
typedef long long ll;

int n;//物品数量
int w[100+5];//物品质量
int v[100+5];//物品价值
int W;//背包总总质量

//i代表物品编号,j代表背包剩余质量
int dfs(int i,int j)
{
    int res;
    if(i==n)
    {
        //已经没有剩余的物品了
        res=0;
    }
    else if(j<w[i])
    {
        //背包可利用价值小于该物品价值
        res=dfs(i+1,j);
    }
    else
    {
        //      不挑选     挑选
        res=max(dfs(i+1,j),dfs(i+1,j-w[i])+v[i]);
    }
    return res;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&w[i],&v[i]);
        }
        scanf("%d",&W);
        printf("%d\n",dfs(0,W));
    }
    return 0;
}

分析:在此做法中,我们因为没有进行剪枝优化,所以进行了多次对同一状态进行搜索,因此我们可以通过利用一个数组dp[i][j]进行存储状态,即所谓的记忆化搜索

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 1<<23
#define mod 1e9+7
typedef long long LL;
typedef long long ll;

int n;//物品数量
int w[100+5];//物品质量
int v[100+5];//物品价值
int W;//背包总总质量

int dp[100+5][10000+5];//i代表物品编号,j代表背包剩余质量,依据题目限制条件写出数组范围

//i代表物品编号,j代表背包剩余质量
int dfs(int i,int j)
{
    int res;
    if(dp[i][j]>=0)
    {
        return dp[i][j];
    }
    if(i==n)
    {
        //已经没有剩余的物品了
        res=0;
    }
    else if(j<w[i])
    {
        //背包可利用价值小于该物品价值
        res=dfs(i+1,j);
    }
    else
    {
        //         不挑选          挑选
        res=max(dfs(i+1,j),dfs(i+1,j-w[i])+v[i]);
    }
    return dp[i][j]=res;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        //初始化dp数组
        memset(dp,-1,sizeof(dp));
        
        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&w[i],&v[i]);
        }
        scanf("%d",&W);
        
        printf("%d\n",dfs(0,W));
        
    }
    return 0;
}

现在我们研究存储状态的记忆化数组,设dp[i][j]存储的是此刻此时背包中左右物品的价值总和,i代表的是我当前已经记录的状态,i+1代表的是背包此时所要面对的物品的编号(假设我是背包,我已经经历过了i号,现在面对的是i+1号,我到底是取还是不取i+1号呢?)

所以我们可以定义

dp[i+1][j]:从0到i这i+1个物品选出总重量不超过j的物品时的总价值

dp[0][j]=0 

所以dp[i+1][j]存在两种状态

第一:dp[i+1][j]=dp[i][j]  (j<w[i])代表当前背包剩余重量已经无法满足存储当前物品重量

第二:dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i])(结合记忆化搜索,可以理解为在选择与不选择这个物品进行一下对比

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 1<<23
#define mod 1e9+7
typedef long long LL;
typedef long long ll;

int n;//物品数量
int w[100+5];//物品质量
int v[100+5];//物品价值
int W;//背包总总质量

int dp[100+5][10000+5];//i代表物品编号,j代表背包剩余质量,依据题目限制条件写出数组范围

void solve()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=W;j++)
        {
            if(j<w[i])
            {
                dp[i+1][j]=dp[i][j];
            }
            else
            {
                dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
            }
        }
    }
    printf("%d\n",dp[n][W]);
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        //初始化dp数组
        memset(dp,0,sizeof(dp));

        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&w[i],&v[i]);
        }
        scanf("%d",&W);

        solve();

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值