I love sneakers!
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4538 Accepted Submission(s): 1866
![](https://i-blog.csdnimg.cn/blog_migrate/ef35afe76885c46ac5bbfbb33553acea.jpeg)
There are several brands of sneakers that Iserlohn wants to collect, such as Air Jordan and Nike Pro. And each brand has released various products. For the reason that Iserlohn is definitely a sneaker-mania, he desires to buy at least one product for each brand.
Although the fixed price of each product has been labeled, Iserlohn sets values for each of them based on his own tendency. With handsome but limited money, he wants to maximize the total value of the shoes he is going to buy. Obviously, as a collector, he won’t buy the same product twice.
Now, Iserlohn needs you to help him find the best solution of his problem, which means to maximize the total value of the products he can buy.
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;
}
分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题,由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题。