还是看了这位大牛的
http://www.chenyajun.com/2010/05/30/4597
题目大意:其实就是个全背包问题~~动态规划
给出每种物品的单价,和个数,然后给出这些物品的组合的优惠策略,要你求出购买这些物品的最小消费。
《编程之美》里有个买书的问题,跟这题很像。
这题难就难在动态规划的维度过多,比如就有3个品种的物品,有种打折策略
dp[i][j][k]表示物品0有i个,物品1有j个,物品2有k个的最小花费, 一种打折策略为物品0, a个与物品1, b个, 与物品 2,c个一起买共花费cost
dp[i][j][k] = dp[i-a][j-b][k-c] + cost,求这些打折策略里面最小的
这个商品的第几维不好去确定,要一个个的去枚举,很麻烦。。。。
考虑状态压缩策略,把每种商品对应的一个维度上去,由于每种商品的个数不超过5个,所以就像对应十进制一样,把它对应到6进制上,比如0商品x1个,1商品x2个,3商品x3个...那么压缩成x1 * 6的0次方 + x2 * 6的一次方 + x3 * 6的2次方 + 。。。 + xn * 6的n-1次方。 这样得到的状态值也可以反过来拆分到各商品分别有多少个。
#include <iostream> #include <cstdio> #include <cmath> #include <map> #include <cstring> #include <algorithm> using namespace std; struct node { int num, price; }; int b, s, st, dp[56656]; node basket[5], offer[100]; map<int, int> dic; //这个用来商品编码映射对应到6进制的第几位 int state[] = {1, 6, 36, 216, 1296, 7776, 46656}; inline bool check(int s1, int s2);//这个是用来计算状态s1和状态s2对应的每种商品的和是否超过了篮子里面的每种商品的个数 inline int cal(int sv);//这个是用来计算状态sv对应的商品花费总和 int main() { memset(dp, -1, sizeof(dp)); dp[0] = 0; st = 0; int index; scanf("%d", &b); for(int i = 0; i < b; i++) { int c; scanf("%d %d %d", &c, &basket[i].num, &basket[i].price); st += state[i] * basket[i].num;//求整个篮子里商品和对应的六进制 dic[c] = i; } scanf("%d", &s); for(int i = 0; i < s; i++) { offer[i].num = 0; int n; scanf("%d", &n); for(int j = 0; j < n; j++) { int c, nu; scanf("%d %d", &c, &nu); index = dic[c]; offer[i].num += state[index] * nu;//第i种打折策略对应的六进制状态 } scanf("%d", &offer[i].price); } for(int i = 0; i < s; i++) { for(int j = 0; j <= st; j++) { if(dp[j] != -1) { if(j + offer[i].num <= st && check(j, offer[i].num))//相当于0/1背包问题,j状态表示对应选好的几种商品的和,与第i中打折策略的商品个数的和不超过总的商品个数,并且确定j状态的对应的第k种商品个数和和打折对应的第k种商品个数和不超过篮子里第k种商品的个数 { if(dp[j + offer[i].num] == -1) dp[j + offer[i].num] = dp[j] + offer[i].price; else dp[j + offer[i].num] = min(dp[j + offer[i].num], dp[j] + offer[i].price); } } } } int ans = 0x7fffffff; for(int i = 0; i <= st; i++) { if(dp[i] != -1) ans = min(ans, dp[i] + cal(st - i));//求出最小的花费 } printf("%d\n", ans); return 0; } inline bool check(int s1, int s2) { for(int i = 0; i < b; i++) { if((s1 % 6 + s2 % 6) > basket[i].num) return false; s1 /= 6; s2 /= 6; } return true; } inline int cal(int sv) { int sum = 0; for(int i = 0; i < b; i++) { sum += (sv % 6) * basket[i].price; sv /= 6; } return sum; }