因为每种物品最多五个,总共五种物品,所以可以将状态存为6进制的整数。
先对每一种special offer进行dp, 转移方程为 dp[ i + offfer[k].stt ] = MIN( dp[ i + offer[k].stt , dp[ i ] + offer[k].prc ), 当然状态转移前,要进行判断是否可转移,
若当前状态下 的某种物品数量加上当前的special offer中的这种物品数量后,超过总共拥有这种物品的数量,此时是不可转移的。
#include <iostream> #include <cmath> using namespace std; #define M 6 struct Item { int num, prc; //num为每种物品的数量,prc为单价 } items[5]; struct Offer { int stt, prc; //stt是将每一种special offer中各种物品压缩成的状态,prc为对应的优惠价 } offer[100]; int hash[1000]; //hash[k]的值为product code为k的物品的下标 int dp[8000]; inline int MIN(int a, int b) { return a > b ? b : a; } //判断当前状态为state时,加上stt(stt为special offer)后,每一种物品是否超过 //这种物品的总数,若超过,则当前状态不能靠这个stt进行转移 bool check(int state, int stt) { int id = 0; while(state > 0 && stt > 0){ if(state % M + stt % M > items[id].num) return false; state /= M; stt /= M; id++; } return true; } int cal_left(int state) { int left = 0, id = 0; while(state > 0) { left += (state % M) * items[id].prc; state /= M; id++; } return left; } int main() { int state; int b, s, n; int i, j; int code, num; state = 0; memset(hash, -1, sizeof(hash)); scanf("%d", &b); for(i = 0; i < b; i++){ scanf("%d%d%d", &code, &items[i].num, &items[i].prc); hash[code] = i; state += items[i].num * (int)pow(6.0, i*1.0); //篮子里的所有物品压缩为状态state } scanf("%d", &s); for(i = 0; i < s; i++){ offer[i].stt = 0; scanf("%d", &n); for(j = 0; j < n; j++){ scanf("%d%d", &code, &num); offer[i].stt += (int)pow(6.0, hash[code]*1.0) * num; //压缩special offer } scanf("%d", &offer[i].prc); } for(i = 1; i <= state; i++) dp[i] = INT_MAX; dp[0] = 0; //注意初始化 for(i = 0; i <= state; i++){ if(dp[i] == INT_MAX) continue; for(j = 0; j < s; j++){ if(i+offer[j].stt <= state && check(i, offer[j].stt)){ //状态之间可以直接相加(以前我不知道这点),判断是否可转移 dp[i+offer[j].stt] = MIN(dp[i+offer[j].stt], dp[i]+offer[j].prc); } } } //计算剩余的按原价购买的物品与通过special offer购买的物品的总价格 //最小值即为答案 int ans = INT_MAX; for(i = 0; i <= state; i++){ if(dp[i] == INT_MAX) continue; ans = MIN(ans, dp[i] + cal_left(state-i)); } printf("%d\n", ans); return 0; }