打牌第二天之完全背包

背包九讲边看边打

打牌小技巧
求背包体积恰好装满,或者背包不用装满的最大价值,这两种只是在dp数组>的初始化中有点不同
体积恰好装满,dp[0]=0,其他dp[i]都为-inf.可以这样想,只有体积为0的时候才允许背包为空,其他的都为不合法状态,为-inf。这样dp[v]就是由合法状态转移过来的了。
体积不用装满。初始化dp都为0。可以这样想,无论体积为啥,都允许,状态为空,什么都不装。

完全背包
题目 有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可取。第 i 种物品的费用是 c[i] ,价值是 w[i] 。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

这个和昨天的01背包很类似,但是有一点不同。这里的物品都有无限件可以取。01背包,每一件物品只能取一个。01背包的做法是,对于当前物品是考虑取或者不取更优。如果这里和01背包一样的做法的话,就要考虑取 0个,取1,2,3,…个
dp[i]=max(dp[i],dp[i-k*c[i]]+k*w[i]);
时间复杂度为O(n*v*max(v/c[i]));

怎么优化时间复杂度?
1.转化为01背包,将第i个物品拆成v/c[i]个i物品,然后用01背包?….这样时间复杂度并没有什么改变…空间复杂度反而增加
2.因为第i个物品最多取v/c[i]个嘛,于是将第i个物品拆成费用为 c[i]2k ,价值为 w[i]2k ,因为任何数量都可以由二进制组成嘛。时间复杂度O(n*v*log(v/c[i]))
3.时间复杂度为O(v*n)的做法。

for(int i=1;i<=n;i++)
{
    for(int j=c[i];j<=v;j++)
        dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
}

POJ 2229

题意:给一个数n,问有多少种组合加起来等于n,组合中的数都是2的幂次。 ans mod 1e9

n<1e6,

分析:n的范围比较小,不超过 220 ,所以有20种数可以组合。每个数都可以任意取。完全背包问题。

状态:dp[i][j]:前i种数,和为j的组合数。
状态转移:
dp[i][j]=dp[i-1][j- 2i ]+dp[i-1][j]+dp[i][j- 2i ];
因为任意取,所以还加上dp[i][j- 2i ];
j的循环从小到大。为了节省空间复杂度,可以把状态转化成一维的。

这里注意。不能用long long 否则会超时。。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>

using namespace std;
const int maxn = 1000005;
int dp[maxn];
int n;
int a[25];
int g=1000000000;
int pow1(int k)
{
    int sum=1;
    for(int i=1;i<=k;i++)
        sum*=2;
    return sum;
}
void solve()
{
    for(int i=0;i<21;i++){
        a[i+1]=pow1(i);
        if(a[i+1]>1000000) break;
    }
    dp[0]=1;
    for(int i=1;i<=21;i++)
    {
        if(a[i]>n) break;
        for(int j=0;j<=n;j++)
        {
            if(j>=a[i])
            dp[j]=(dp[j]+dp[j-a[i]])%g;
        }
    }
    printf("%d\n",dp[n]%g);
}
int main()
{
    scanf("%d",&n);
    solve();
    return 0;
}

还有另外一种方法。时间复杂度为O(n)。点这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值