Charm Bracelet(POJ 3624)

题目链接http://poj.org/problem?id=3624
题目大意:01背包问题
N个物品,每个物品有两个属性,v(价值),w(重量),且每个物品只有一个。现有背包容量为M,求背包可承受范围内的最大价值。
题解
DP(Dynamic Programming)即动态规划。每一步的选择会影响之后的状态。
我们设
状态3:dp[j] ->前i个物品在剩余容量为j的情况下的最大价值。
状态2:dp[j] ->没有选择物品i(前i-1个物品在容量为j的情况下的最大价值)
状态1:dp[j-item[i].w]+item.v ->选择了物品i(最大价值加上物品i的价值,容量减去物品i的重量)
状态3是状态1、2中的较大者。

个人见解:
1、首先我们需要对item按照w从小到大排序,我们要从重量小的物品开始选择。
为什么呢?如果我们从大的开始选择物品往背包里放,或者不排序就开始选择物品往背包里放。以前者为例:我们首先选择最大的重量的物品,但是装不满背包我们就需要再选择j-item[i].w容量内的物品,这时候dp[j-item[i].w]是0,因为我们初始化是0,并且我们先选择的是重量大的物品,也就有可能造成答案错误。

嗯,这里是错的,请看下面。
2、

for(int i=1;i<=N;i++)
       for(int j=M;j>=item[i].w;j--)
       {
           dp[j]=max(dp[j],dp[j-item[i].w]+item[i].v);
       }

这里的循环:
对第i个物品选择放与不放来保证dp是各个容量下当前的最大价值。就是像这样不断更新dp,最终得到答案。

3、内层循环从最大容量开始跑是为了避免重复选择物品。
(如果是这样:for(int j=item[i].w;j<=;j++),那么dp[item[i].w]状态选择了i物品是最大价值,那么dp[item[i].w*2]状态仍可能是选择i物品是最大价值,这种情况就是完全背包(物品的数量无限))

4、多重背包:物品的数量有限且数量不定。
这个时候就只需要将每个物品的数量用num数组存起来,然后再加一层循环:
也就是将2A看成A和A的叠加

for(int i=1;i<=N;i++)
     for(int k=1;k<=num[i];k++)
            for(int j=M;j>=k*item[i].w;j--)
            {
                dp[j]=max(dp[j],dp[j-item[i].w]+item[i].v);
            }

1item[i].w
2
item[i].w//上边跑完之后,(item[i].w+1)->2item[i].w是不会再次放物品i的
3
item[i].w//与上边类似
……
kitem[i].w//与上边类似
所以直接将这中间不会放物品i的过程省略优化掉,就加了一个k
item[i].w,不加就会多跑一些时间。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
const int maxc=12888;//max_capacity->最大容量
const int maxN=3500;

int dp[maxc];
struct node
{
    int v,w;
    node(int a=0,int b=0):v(a),w(b){}
    friend bool operator < (node n1,node n2)
    {
        return n1.w<n2.w;
    }
}item[maxN];

int N,M,ans;
void init()
{
    memset(dp,0, sizeof(dp));
    ans=0;
}

int main()
{
    while(~scanf("%d%d",&N,&M))
    {
        init();
        for(int i=1;i<=N;i++)
        {
            scanf("%d%d",&item[i].w,&item[i].v);
        }
        for(int i=1;i<=N;i++)
        {
            for(int j=M;j>=item[i].w;j--)
            {
                dp[j]=max(dp[j],dp[j-item[i].w]+item[i].v);
            }
        }
        for(int i=M;i>=1;i--)
            ans=max(ans,dp[i]);
        printf("%d\n",ans);
    }
    return 0;
}







猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/94599474