因为每种物品最多五个,总共五种物品,所以可以将状态存为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;
}