您的位置:首页 > 其它

hdu3033(分组背包二次dp处理)

2013-07-09 20:25 344 查看

I love sneakers!

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2895    Accepted Submission(s): 1185


Problem Description

After months of hard working, Iserlohn finally wins awesome amount of scholarship. As a great zealot of sneakers, he decides to spend all his money on them in a sneaker store.



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.
 

Input

Input contains multiple test cases. Each test case begins with three integers 1<=N<=100 representing the total number of products, 1 <= M<= 10000 the money Iserlohn gets, and 1<=K<=10 representing the sneaker brands. The following N lines each represents a
product with three positive integers 1<=a<=k, b and c, 0<=b,c<100000, meaning the brand’s number it belongs, the labeled price, and the value of this product. Process to End Of File.
 

Output

For each test case, print an integer which is the maximum total value of the sneakers that Iserlohn purchases. Print "Impossible" if Iserlohn's demands can’t be satisfied.
 

Sample Input

5 10000 3
1 4 6
2 5 7
3 4 99
1 55 77
2 44 66

 

Sample Output

255

 

题意:用M元买鞋,每种鞋有编号,价钱,自己喜欢程度三个值,要求所给编号每种至少买一双,使得喜欢程度的和最大。就是个分组背包,加了一个条件限制。搜到题解,看了一天多啊。。。看不懂啊。。。问了好多人一样看不懂啊。。。。今天和大神聊天。。。大神一语点醒我。dp啊,果然就是dp。感慨完了。。。继续正题……
    下面就不管鞋不鞋的问题了,直接按照背包讲,钱是容量,满意程度就是价值。状态定义是这样的dp[i][v]代表前i组里面,每组至少选一个,装在容量为v的背包里面可以得到的最大值。这样从什么都没有dp[0][0~V] = 0的合法状态往外拓展(即往外状态转移),不合法状态赋值-1。如果最后dp[k][V]等于-1那就是这个状态根本达不到,那就是不能满足要求。(注意如果给定的编号不齐全,那就直接输出不可能就好了)。这样说一开始就理解但是,我看不懂代码,同时我伴随着一种疑问也解决不了。这些具体在代码中讲解。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define rep(i,n) for(int i = 0;i < n;i ++)
struct node
{
int v,val;
}tmp;
int dp[11][10101];
int main()
{
int n, V, k, cnt;
while(~scanf("%d%d%d",&n,&V,&k))
{
bool hash[11];cnt = 0;//这行变量用于判断所给编号是否齐全
vector<node>gp[11];//用于储存各个组的背包内容
memset(hash,0,sizeof(hash));

rep(i,n)
{
int num;
scanf("%d%d%d",&num,&tmp.v,&tmp.val);
gp[num].push_back(tmp);
if(!hash[num])cnt++,hash[num] = true;
}
if(cnt != k){
puts("Impossible");
}else{

memset(dp,-1,sizeof(dp));//全部初始化不合法
memset(dp[0],0,sizeof(dp[0]));//前零组就是什么都没有的时候,,所以各种容量值都为0是合法的
//开始是担心之后放进去的物品由于价值更大而取代了前面某组可能只有一件放入的物品,这样就不符合要求了


//但其实不会这样,因为当前能够拓展出状态的条件就是前i-1组已经都至少每组都放进去了,在这种情况下有空间放下


//当前物品,那么这一新状态也就是合法的状态了。i最小为1,i-1为0,0状态的时候都合法。这样一推就都对了。


//其实这一思路是对于每一个组的物品进行0-1背包,只是多了个条件,它必须在可以被前一组拓展的基础上才可以进行0-1背包的操作,否则当前组的物品是不能进行0-1背包的
for(int i = 1;i <= k;i ++)//枚举组

for(int j = 0;j < gp[i].size();j ++)//枚举组内各种物品

for(int v = V;v >= gp[i][j].v;v --)//0-1背包,有点特别
{//这两个的顺序是不能交换的,因为第一个if语句代表普通0-1背包

		//注意这个不是把滚动数组省去了的意思,多出的一维是描述前几个组的
if(dp[i][v - gp[i][j].v] != -1)
dp[i][v] = max(dp[i][v],dp[i][v - gp[i][j].v] + gp[i][j].val);

//这个就是描述把当前物品放入前面组的做法,之所以不能颠倒,是因为当前物品只能

		//放一次,如果颠倒顺序就会给当前物品有一个在本次(前i组)背包里面新增一个合法状态,

		//如果该物品恰好可以拓展该状态,那么就放了两次了,所以不行。dp[i][0~V]的合法

		//状态除了dp[0][0~V]是赋值得到,都必须是从前一组里面拓展得到的这样才可以保证得到

 		//的一切状态符合要求。
if(dp[i - 1][v - gp[i][j].v] != -1)
dp[i][v] = max(dp[i][v],dp[i - 1][v - gp[i][j].v] + gp[i][j].val);
}

if(dp[k][V] != -1)
printf("%d\n",dp[k][V]);
else puts("Impossible");
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: