zoukankan      html  css  js  c++  java
  • [luogu p1616] 疯狂的采药 & 完全背包学(复)习笔记

    传送门

    疯狂的采药

    题目背景

    此题为NOIP2005普及组第三题的疯狂版。 此题为纪念LiYuxiang而生。

    题目描述

    LiYuxiang是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:

    "孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。"

    如果你是LiYuxiang,你能完成这个任务吗?

    此题和原题的不同点:

    1. 每种草药可以无限制地疯狂采摘。
    2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

    输入输出格式

    输入格式

    输入第一行有两个整数T(1 <= T <= 100000)和M(1 <= M <= 10000),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到10000之间(包括1和10000)的整数,分别表示采摘某种草药的时间和这种草药的价值。

    输出格式

    输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

    输入输出样例

    输入 #1

    70 3
    71 100
    69 1
    1 2
    

    输出 #1

    140
    

    说明/提示

    对于30%的数据,M <= 1000;

    对于全部的数据,M <= 10000,且M*T<10000000(别数了,7个0)。

    加油LiYuxiang,第一个AC留给你!

    分析

    此题是一道裸完全背包。
    01背包不同的是,完全背包一种东西可以无限制的取用。那么假设我采了(n)遍这次草药,那么这种情况下的状态转移方程应该是dp[i][j] = dp[i - 1][j - n * v[i]] + n * w[i]
    如果我们取整体的状态转移方程,套个(max)就可以了:
    dp[i][j] = max(dp[i][j],dp[i - 1][j - n * v[i]] + n * w[i])
    不过如果这样,时间复杂度就是(O(MT^T)),此题的数据范围是(M le 10000)(T le 100000),所以你用这种方法能T飞。

    那怎么办?

    只需要把01背包中内部循环j的逆序改为顺序即可。如果你不知道01背包的一维优化中为什么是逆序,见我写的这篇题解

    然后这个又怎么跟01背包扯上关系呢?首先01背包所依赖的子结果为dp[i - 1][j - v[i]],因为01背包只能选和不选两种,因此选的状态转移方程中不能含有当前的第i件物品。也就是说,缩成一维的时候要保证dp[j - v[i]]逆序遍历,才能保证遍历dp[j]的时候dp[j - v[i]]还是上一层的状态(未更新的状态)。

    但是完全背包可不一样,爱选多少选多少,所以我们考虑加选一件第i种物品的策略,就需要考虑dp[i][j - v[i]]这种子结果。也就是说,完全背包的二维版本中,状态转移方程把dp[i - 1][j - v[i]]变为了dp[i][j - v[i]]。这次缩成一维,遍历到dp[j]的时候,要要求dp[j - v[i]]是更新的。这就需要正序遍历啦~

    也就是说,就是一个倒序变正序。神奇吧?这就是dp的魅力。

    代码

    /*
     * @Author: crab-in-the-northeast 
     * @Date: 2020-03-12 20:54:13 
     * @Last Modified by: crab-in-the-northeast
     * @Last Modified time: 2020-03-13 01:28:22
     */
    #include <iostream>
    #include <cstdio>
    
    const int maxm = 10005;
    const int maxt = 100005;
    
    inline int max(int a,int b) {
        return a > b ? a : b;
    }
    
    int v[maxm],w[maxm],dp[maxt];
    
    int main() {
        int T,M;
        std :: cin >> T >> M;
        for(int i = 1; i <= M; i++) 
            std :: cin >> v[i] >> w[i];
        
        for(int i = 1; i <= M; i++)
            for(int j = v[i]; j <= T; j++)
                dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
            
        std :: cout << dp[T] << std :: endl;
        return 0;
    }
    

    评测结果

    RE 44R31673389(原因:看错数据范围……弄混数组大小……)
    AC 100R31673423(这个代码虽然AC了但是关于数组大小方面有点小问题)
    AC 100R31680243

  • 相关阅读:
    POJ 3373 Changing Digits 记忆化搜索
    POJ 3268 Silver Cow Party (Dijkstra + 优先队列)
    ZOJ 1232 Adventure of Super Mario (Floyd + DP)
    POJ 2406 Power Strings KMP算法之next数组的应用
    POJ 1961 Period KMP算法之next数组的应用
    POJ 2492 A Bug's Life 并查集的应用
    POJ 1703 Find them, Catch them 并查集的应用
    POJ 3321 Apple Tree 树状数组+DFS
    POJ 3368 Frequent values 线段树与RMQ解法
    POJ 3264 Balanced Lineup RMQ ST算法
  • 原文地址:https://www.cnblogs.com/crab-in-the-northeast/p/luogu-p1616.html
Copyright © 2011-2022 走看看