首页 > 代码库 > hdoj 2126 Buy the souvenirs 【另类01背包】

hdoj 2126 Buy the souvenirs 【另类01背包】

题意:求最多购买的件数以及有几种方法。

一看到这题就想到了背包,因为求得是种类数,所以我们可以将件数看做价值,将价格看做重量,这就变成01背包了(dp),但是还要求有几种购买方案,那么再来一个背包(kind)。

分析:有三种情况:

1》dp[j] < dp[j-s[i]]+1

那么对于这一种情况  方案背包的状态转移方程是kind[j] = kind[j-s[i]]?kind[j-s[i]]:1;(考虑到kind[j-s[i]] ==0的时候,这时候kind[j] = 1);

证明:为什么是kind[j] = kind[j-s[i]];假设原来的dp[j-s[i]]是2,为AB, AC,那么再买一件D就变成了ABD和ACD 所以... 

2》dp[j] == dp[j-s[i]]+1

那么对于这一种情况显然 dp[j] +=  kind[j-s[i]]?kind[j-s[i]]:1;(考虑到kind[j-s[i]] ==0的时候,这时候kind[j] += 1)

证明:dp[j] == dp[j-s[i]]+1 ,虽然两者相等,但是构成不一样(例如:dp[j] = 2 为AC AB, 但是dp[j-s[i]] 为 CD, BD(仔细想一下!!!)),所以状态转移方程就是dp[j] +=  kind[j-s[i]]?kind[j-s[i]]:1

3》 

代码:dp[j] > dp[j-s[i]]+1

对于此种情况  不用考虑交换.

#include <stdio.h>
#include <string.h>
#include <algorithm>
using std::sort;
#define M 550
int dp[M];
int kind[M];
int s[M];
int n, m;
bool cmp(int a, int b){
	return a<b;
}
void DP(){
	memset(dp, 0, sizeof(dp));
	memset(kind, 0, sizeof(kind));
	int i, j;
	for(i = 0; i < n; i ++){
		for(j = m; j >= s[i]; j --){
			if(dp[j] < dp[j-s[i]]+1){  //第一种情况
				dp[j] = dp[j-1]+1;
				if(kind[j-s[i]]) kind[j]=kind[j-s[i]];  //<span style="font-size:14px;">kind[j] = kind[j-s[i]]?kind[j-s[i]]:1</span>
				else kind[j] = 1;
			}
			else if(dp[j] == dp[j-s[i]]+1){   //第二种情况
				if(kind[j-s[i]]) kind[j] += kind[j-s[i]]; //<span style="font-size:14px;">kind[j] += kind[j-s[i]]?kind[j-s[i]]:1</span>
				else kind[j] += 1;
			}
		}
	}
}
int main(){
	int t, i, j;
	scanf("%d",&t);
	while(t --){
		scanf("%d%d", &n, &m);
		for(i = 0; i < n; i ++){
			scanf("%d", &s[i]);
		}
		int ans = 0, temp = m;
		sort(s, s+n, cmp);
		DP();
		if(dp[m] == 0){
            printf("Sorry, you can't buy anything.\n");
        }
        else
        printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n", kind[m], dp[m]);

	}
	return 0;
}

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2126

hdoj 2126 Buy the souvenirs 【另类01背包】