5 10000 3 1 4 6 2 5 7 3 4 99 1 55 77 2 44 66
255
题意:
Iserlohn 有M元钱,现在有N双鞋子,鞋子有K个品牌,每双鞋都有三个参数品牌a,标价b,还有一个价值c,问Iserlohn 是否可以把每种品牌的鞋子至少买一双,如果不可以,输出“impossible·”,可以输出可以获得鞋子的最大价值和。
分析:
首先看一下什么是分组背包。
有N件物品和一个容量为V的背包。第i件物品的费用是Ci,价值是Wi。这些物品被划分为K组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
我们注意到,分组背包是每组只能最多选出一个物品,而此题是要求每组至少要选出一个,但是,可以说,思路还是大致类似。
设dp[k][v] 表示选前k组物品我用钱数为v 的情况下能取到的最大价值总和。这个题不仅要求最大的价值总和,还要判断可行性,那么我们用-INF或者-1来初始化dp数组
状态转移方程为:
dp[k][v] = max{ dp[k][v], max{ dp[k][v - shoes[k][i].price] , dp[k - 1][v - shoes[k][i].price] } + shoes[k][i].value | item i ∈ group k}
这道题有滚动数组的解法,只需要dp[2][10000+5],但是滚动数组在此题优化不是特别明显,读者自行百度搜索滚动数组的方法,我就不给出来了。空间允许的情况下,我觉得开个二维的数组还是更好,直观不易错,当然,滚动数组的做法在DP中也是很重要的。
#include <vector> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef __int64 LL; const int maxk = 10 + 1; const int maxn = 100 + 5; const int maxm = 10000 + 5; int N, M, K; int dp[maxk][maxm]; struct Shoe { int price, value; Shoe() {} Shoe(int p, int v) : price(p) , value(v) {} } ; vector<Shoe> shoes[11]; int main() { //freopen("input.in", "r", stdin); while(~scanf("%d %d %d", &N, &M, &K)) { int brand, price, value; for(int k = 0; k <= K; k++) shoes[k].clear(); for(int i = 0; i < N; i++) { scanf("%d %d %d", &brand, &price, &value); shoes[brand].push_back(Shoe(price, value)); } //下面的代码是整道题的关键,那么我就在下面代码的注释中给大家累赘一下 ...O(∩_∩)O哈哈~ //这里必须把dp初始化为-1,或者-INF,dp[k][v] = -1 OR -INF 这是表示前k组用钱数为v还不能取到任何鞋。 memset(dp, -1, sizeof(dp)); // 没有一组的时候,无论你有多少money,当然此时的最大价值为0了 for(int v = 0; v <= M; v++) dp[0][v] = 0; //或者memset(dp[0],0,sizeof(dp[0]); //下面这三层循环保证了每一组中至少一个物品会被添加到背包中 //首先,对组数进行枚举 for(int k = 1; k <= K; k++) { //然后,对第k组的所有元素进行枚举 for(int i = 0; i < shoes[k].size(); i++) { // //为什么递减呢?这里是跟01背包滚动数组的实现一样的,因为第k组第i个物品只能选择一次。 for(int v = M; v >= shoes[k][i].price; v--) { //下面这个是判断当前选择的情况下,我这个品牌是否已经选过一次了,如果选过,我还可以继续选下去 if(dp[k][v - shoes[k][i].price] != -1) dp[k][v] = max(dp[k][v], dp[k][v - shoes[k][i].price] + shoes[k][i].value); if(dp[k - 1][v - shoes[k][i].price] != -1) dp[k][v] = max(dp[k][v], dp[k - 1][v - shoes[k][i].price] + shoes[k][i].value); } } } //如果dp[K][M] 为-1 ,那么证明没有可行解 if(dp[K][M] < 0) printf("Impossible\n"); else printf("%d\n", dp[K][M]); } return 0; }
分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题,由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题。