POJ3624---01背包入门 dp+滚动数组+重复利用一维数组

题意:01背包入门题(入门必做)
思路:dp,用二维数组会MLE(超内存),由于每个状态只与前一状态有关,故可开滚动数组压缩空间,也可以重复利用一个一维数组。
dp[i][j]代表从前i个物品中选出总重量不超过j的物品时总价值的最大值。则
dp[i][j]=max{dp[i-1][j],dp[i-1][w[i]-j]+v[i]} (w[i]<=j)
dp[i][j]=dp[i-1][j] (w[i]>j)
边界控制:
dp[i][j]=0 (i==0||j==0)由于定义全局数组,所以在代码中不必再赋值。

本题作为dp背包问题的入门题,值得新手去做,如果觉得不能理解递推关系,滚动数组,重复利用一维数组…~
可以先把二维数组里的值一步一步给画出来(我看的挑战程序设计那本书上有这种图),然后你就会明白所以然了。可以打印下:(斜线代表取,直线代表不取。然后再用这个图理解下滚动数组和一维数组。测试数据为poj上的。)斜线代表取,直线代表不取。然后再用这个图理解下滚动数组和一维数组
说下注意的问题:
第二个for循环,可以正序,也可以逆序,因为dp[i+1][…]里的东西都是从dp[i]中得出来的。
但是带来的问题就是内存超了。
滚动数组也是可以正序,可以逆序。
但是重复利用一维数组就必须要逆序了,因为得到后面的必须要用到前面的~~~这让你想起了什么???记得刚开始学程序设计的时候,老师让打印杨辉三角,说用一维数组打印试试~(实际上,杨辉三角也是动态规划问题咯)
说明2:
滚动数组:
当前状态都来源于上一个状态,所以这时候只需要两个数组就够了。拿经典的01背包问题来说,第二行只和第一行有关,第三行只和第二行有关……所以只需要两行就
可以了。当然,有的复杂的dp问题i和i-1,i-2有关,这时候要开三行数组,i%3

一维数组:
主要是顺序还是逆序的问题,如果当前状态来源于“上”和“左斜上”(01背包)那么要逆序了,自己一定要明白为什么要这样 , 如果当前状态来源于“←”和“↖”(完全背包)那就要顺序了。

如果当前状态来源于“←”,“↑”和“↖”,这时候就不能用一维数组来优化了,必须要用滚动数组。

说明3:
并非所有的问题都要去优化空间,有的要输出路径,这时候就不能压缩空间了,需要保留所有的数据。比如这两道题:
1 2

三种代码:(依次为原始二维数组,滚动二维数组,一维数组)

#include<iostream>
#include<algorithm>
using namespace std;
int dp[3500][13000];//0
int w[3500], v[3500];
int main()
{
    int N, M; cin >> N >> M;
    for (int i = 1; i <= N; i++)
        cin >> w[i] >> v[i];
    for(int i=1;i<=N;i++)
        for (int j = 0; j <= M; j++)
        {
            if (j < w[i])dp[i][j] = dp[i - 1][j];
            else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
     }
    cout << dp[N][M];
    system("pause");
}
#include<iostream>
#include<algorithm>
using namespace std;
int dp[2][13000];//0
int w[3500], v[3500];
int main()
{
    int N, M; cin >> N >> M;
    for (int i = 1; i <= N; i++)
        cin >> w[i] >> v[i];
    for(int i=1;i<=N;i++)
        for (int j = 0; j <= M; j++)
        {
            if (j < w[i])dp[i%2][j] = dp[(i - 1)%2][j];
            else dp[i % 2][j] = max(dp[(i - 1) % 2][j], dp[(i - 1) % 2][j - w[i]] + v[i]);
     }
    cout << dp[N%2][M];
    system("pause");
}
#include<iostream>
#include<algorithm>
using namespace std;
int dp[13000];//0
int w[3500], v[3500];
int main()
{
    int N, M; cin >> N >> M;
    for (int i = 1; i <= N; i++)
        cin >> w[i] >> v[i];
    for(int i=1;i<=N;i++)
        for (int j = M; j >= w[i]; j--)   //j >= w[i]这个结束条件是从 j>0和下面的if(j >= w[i])语句得来的。
        {
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
         }
    cout << dp[M];
    system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值