背包问题

 
  

0-1背包变形,TAOTAO要吃鸡

牛客网,题目链接:https://www.nowcoder.com/acm/contest/74/B

题目描述

Taotao的电脑带不动绝地求生,所以taotao只能去玩pc版的荒野行动了,
和绝地求生一样,游戏人物本身可以携带一定重量m的物品,装备背包
之后可以多携带h(h为0代表没有装备背包)重量的东西。玩了几天
taotao发现了一个BUG,当装备背包之后,如果可携带重量没有满,就
可以拿一个任意重的东西。(解释看样例)有一天taotao空降到了一个
奇怪的岛上,岛上有n件装备,每个装备都有重量Wi和威力值Vi,但taotao
不认识这些装备,所以他来求助你,挑选威力最大的装备,帮助他吃鸡。

输入描述:

本题有多组输入(小于10),当n=0时结束输入。
第一行输入n,m,h。n,m,h为整数,并且0<=n,m,h<=100,
接下来n行,每行输入第i个物品的物品的重量Wi和威力值Vi。0<=Wi,Vi<=100.

输出描述:

输出最大威力值,每组输出一行。
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        // 0-1背包问题的变形
        /**
         * 切入点是是否存在bug,与h的值有关
         * h = 0 ,直接0-1背包
         * h !=0 ,假设因为bug携带上的装备是第k件,那么,对剩下的问题求解,背包容量为m+h-1
         */
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            int n = scan.nextInt(); // n件装备
            if (n == 0) {
                break;
            }
            int m = scan.nextInt(); // 自身可携带m
            int h = scan.nextInt(); // 背包可携带
            int[] w = new int[n]; // 重量
            int[] v = new int[n]; // 威力
            for (int i = 0; i < n; i++) {
                w[i] = scan.nextInt();
                v[i] = scan.nextInt();
            }

            if (h == 0) {
                /**
                 * 不存在bug
                 * 设dp[i][j]表示,拿前i件装备中的一部分,总重量为j,带来的威力值
                 * dp[i][j] = max{dp[i-1][j], dp[i][j-w[i]]+v[i]};
                 * 由于i的变化在1以内,因此可以省掉这部分空间
                 * 防止重装,关于重量的计算要从大到小
                 */

                int dp[] = new int[m + h + 1];
                dp[0] = 0;
                for (int i = 0; i < n; i++) {
                    for (int j = m; j >= w[i]; j--) {
                        dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
                    }
                }
                System.out.println(dp[m]);
            } else {
                /**
                 * 存在bug,假设第k件装备是利用bug放进背包的
                 * 也是0-1背包求解,重量阈值改为m+h-1,只考虑拿i-1件装备
                 */
                int max = 0;
                for (int k = 0; k < n; k++) {
                    int dp[] = new int[m + h];
                    dp[0] = 0;
                    for (int i = 0; i < n; i++) {
                        if (i == k) {
                            continue;
                        }
                        for (int j = m + h - 1; j >= w[i]; j--) {
                            dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
                        }
                    }
                    //System.out.println(String.format("max: %d, vi: %d", dp[m + h - 1], v[k]));
                    max = Math.max(max, dp[m + h - 1] + v[k]);
                }
                System.out.println(max);
            }
        }
    }
}

完全背包问题:小猪钱罐

POJ,题目链接:http://poj.org/problem?id=1384

Description

Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid.

But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!

Input

The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it’s weight in grams.

Output

Print exactly one line of output for each test case. The line must contain the sentence “The minimum amount of money in the piggy-bank is X.” where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line “This is impossible.”.

Sample Input

3
10 110
2
1 1
30 50
10 110
2
1 1
50 30
1 6
2
10 3
20 4

Sample Output

The minimum amount of money in the piggy-bank is 60.
The minimum amount of money in the piggy-bank is 100.
This is impossible.

import java.util.Scanner;

/**
 * 有n 种硬币且已知每种硬币的价值和重量,每种硬币可以无限次使用.
 * 存储罐在盛硬币之前的重量为v1,之后为v2,问存储罐中存的硬币最小价值可能为多少.
 * 若当前情况不可能存在,输出”This is impossible.”.
 */
public class PiggyBank {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int T = scan.nextInt(); // T组case
        while (T-- > 0) {
            int v1 = scan.nextInt(); // before
            int v2 = scan.nextInt(); // after
            int kinds = scan.nextInt(); // 钱币种类数
            int[] coins = new int[kinds]; // 面额
            int[] w = new int[kinds]; // 重量
            for (int i = 0; i < kinds; i++) {
                w[i] = scan.nextInt();
                coins[i] = scan.nextInt();
            }

            /**
             * 完全背包问题
             * 设dp[i][j]表示只用前i种钱币,总价值为j,带来的重量
             * 由题意,dp[i][j] = min{dp[i-1][j],dp[i][j-coins[i]]+w[i]}
             * 用滚动数组可以省去一维空间
             * 由于硬币可以重复使用,因此j从前向后计算
             */
            int aim = v2-v1; // 目标重量
            int dp[] = new int[aim+1];
            for (int i = 1; i < aim+1; i++) {
                dp[i] = Integer.MAX_VALUE;
            }
            for (int i = 0; i < kinds; i++) {
                for (int j = coins[i]; j <= aim; j++) {
                    if(dp[j-coins[i]] ==Integer.MAX_VALUE) continue;
                    dp[j] = Math.min(dp[j], dp[j-coins[i]]+w[i]);
                }
            }
            if(dp[aim] == Integer.MAX_VALUE) {
                System.out.println("This is impossible.");
            } else {
                System.out.println("The minimum amount of money in the piggy-bank is " + dp[aim]);
            }
        }
    }
}


你可能感兴趣的:(Arithmetic)