HDU 2955, 3496 兼作01背包总结

5 篇文章 0 订阅
3 篇文章 0 订阅

HDU 2955

这道题如果把被抓概率当作背包容量,将会十分麻烦,因为概率不一定是2位小数,放大后空间和时间上都过不去。这时候需要转化,我们可以求盗取i单位钱不被抓获的的可能性最大值,容量变为盗取的钱数,问题就好解决了。

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        double p;
        int n;
        scanf("%lf %d", &p, &n);
        int sum = 0;
        double c[105];
        int v[105];
        for(int i = 1; i <= n; i++){
            scanf("%d %lf", &v[i], &c[i]);
            sum += v[i];
            c[i] = 1 - c[i];
        }
        double dp[10005];
        for(int i = 1; i <= 10004; i++) dp[i] = 0;
        dp[0] = 1;
        for(int i = 1; i <= n; i++)
            for(int j = sum; j >= v[i]; j--)
            dp[j] = max(dp[j], dp[j - v[i]] * c[i]);
        int res = 0;
        for(int i = sum; i >= 0; i--){
            if(dp[i] - 1 + p > 1e-6){
                res = i;
                break;
            }
        }
        printf("%d\n", res);
    }
    return 0;
}

HDU3496

这个01背包扩展到了二维。题目限定必须购买某个数量的物品,不能多不能少。这种限定条件的应对策略是将dp[0][0...V]初始化为0,其他初始化为负无穷大。这样做的理由是:如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int INF = 999999;
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        int n, m, l;
        scanf("%d %d %d", &n, &m, &l);
        int c[105], v[105];
        for(int i = 1; i <= n; i++) scanf("%d %d", &c[i], &v[i]);
        int dp[105][1005];
        memset(dp, -INF, sizeof(dp));
        for(int i = 0; i <= l; i++) dp[0][i] = 0;
        for(int i = 1; i <= n; i++)
            for(int j = m; j >= 1; j--)
            for(int k = l; k >= c[i]; k--)
            dp[j][k] = max(dp[j][k], dp[j - 1][k - c[i]] + v[i]);
        if(dp[m][l] < 0) dp[m][l] = 0;
        printf("%d\n", dp[m][l]);
    }
    return 0;
}<strong>
</strong>

做了几天的01背包,这里做个小结。

1.循环的起始和顺序

for(int i = 1; i <= n; i++)
            for(int j = sum; j >= c[i]; j--)
            dp[j] = max(dp[j], dp[j - c[i]] + v[i]);
这是01背包最基本的循环。它可行的原因是:对第i个物品进行选择时,前i-1个物品已经处理完毕,并且dp[j - c[i]]已经得到。因此必须先对物品编号进行循环,而占用的容量必须从大到小枚举。


2.如果限定背包必须装满,可以参考HDU3496的应对策略。


3.一个问题中的容量、价值不是固定的,确定何者为容量,何者为价值,往往是解决难题的关键。对复杂的问题,必须要化归为基本的背包问题。


4.一点体会:dp很抽象,而且一个状态转移方程摆出来,有的看似神奇,需要慢慢揣摩;有的看似理所当然,实则需要分析它的循环起始、初始化等等,copy一遍当然简单,但是理解不了精髓。一个比较好的学习方法是:打印出每个状态,看看每一步发生了什么,状态是如何转移的,想想为什么这个方程可行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值