动态规划--(01背包 poj3624 )

博客介绍了01背包问题的背景和动态规划求解方法,通过一个旅行者背包的例子阐述问题,并讨论了如何避免无用功,给出了具体的递推公式和Fk(y)的计算表,最后提到了POJ3624题目与解题思路的相似性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于背包问题:

众所皆知,这里讲给出关于背包问题的一些简介:

一个旅行者准备随身携带一个背包. 可以放入背包的物品有n , 每种物品的重量和价值分别为 wj , vj . 如果背包的最大重量限制是 b, 怎样选择放入背包的物品以使得背包的价值最大

题目就是这么简单:

先思考一个问题:就是每种物品可以重复选择,这种要怎么解决:

Fk(y):装前 k 种物品, 总重不超过 y, 背包的最大价值;

这里的子问题:很好理解,想一想,每一次你要去拿物品,你会想,

如果,只拿一个物品并且包包还能装下的最大价值是多少;

如果,要拿两个物品并且包包能装下的最大价值是多少;

。。。


然而,如果不用动态规划:   每一次,多拿一个物品,就要从第一个物品(前提是包包能装下)去开始拿,,,,你不觉得,这是在做无用功吗?

这个时候动态规划就来了,可以做一个备忘录,记录前面你已经拿过的价值,到某一个物品的时候就可以直接将前面存储的最大价值拿来用;

其实说是这么说,真正的去理解递推公式,还是有一点难的:


v1=1, v2=3, v3=5,v4=9,

w1 =2, w2 =3, w3 =4, w4 =7,

b = 10
Fk(y) 的计算表如下:(利用递推的公式:
Fk ( y) = max{Fk 1 ( y), Fk ( y wk ) + vk



代码:

#include <iostream>
#include <cstring>
#define MM 12882

using namespace std;

#define NUM 100
int main11()//同一个物品物品可以多次选择;
{
    int V[NUM],W[NUM];//表示每个物品的价值和大小;
    int dp[2][MM];
    int N,M;//物品数;和最大重量;
    cin>>N>>M;
    for(int i = 1;i<=N;i++){
        cin>>W[i]>>V[i];
    }

    memset(dp,0, sizeof(dp));



    for(int i = 1;i<=N;i++){
        for(int j = 1;j<=M;j++){
            if(j-W[i]>=0)
                dp[i%2][j] = max(dp[(i-1)%2][j],dp[i%2][j-W[i]]+V[i]);
            else{
                dp[i%2][j] = dp[(i-1)%2][j];
            }
        }

//        for(int j = 0;j<=M;j++)
//            cout <<dp[i%2][j]<<" ";
//        cout <<endl;
    }

    cout <<dp[N%2][M]<<endl;
    return 0;
}

//4 10
//2 1
//3 3
//4 5
//7 9

然而:如果是物品不能重复选择的:

递推方程:Fymax{Fy), Fk-1 − wvk}

思路和上面的一模一样;poj3624

//4 6
//1 4
//2 6
//3 12
//2 7


#include <iostream>
#include <cstring>

#define NN 3204
#define MM 12882

using namespace std;
int main()//同物品不能重选;
{
    int W[NN],V[NN];
    int N,M;
    cin >>N>>M;
    for(int i = 1 ;i<=N;i++)
        cin>>W[i]>>V[i];
        //输如数据;



    int dp[2][MM];
    memset(dp, 0, sizeof(dp));

    for(int i = 1;i<=N;i++){
        for(int j = 1;j<=M;j++){
            if(j-W[i]>=0)
                dp[i%2][j] = max(dp[(i-1)%2][j],dp[(i-1)%2][j-W[i]]+V[i]);
            else
                dp[i%2][j] =dp[(i-1)%2][j];
        }
    }

    cout << dp[N%2][M]<<endl;
    return 0;
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值