版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhlbjtu2016/article/details/82719036
题目链接:http://poj.org/problem?id=1170
题意:有k种物品,每种物品对应若干标号,给出了这k种物品的标号和它们对应的数量和单价。有s种价格优惠方式,每种方式告诉了一些标号和数量以及它们的优惠价格。求恰好购买k种物品及其对应的数量所需的最小费用。
分析:离散化+状态压缩dp+背包思想,因为只有5种物品,每种物品的数量也不超过5,所以想到了状态压缩dp,用六进制,有5位,每一位代表一种物品的数量
先预处理进制,将标号离散化成对应种类的物品,找到所有可能的状态。然后状态压缩,先枚举没有用优惠的,从小到大不超过该种物品数量枚举,然后枚举优惠的,就是看这种优惠方式对应的状态如果可以取,能否使得价格更优。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
int con[5],pri[5];
int ma[1010],state[110],sp[110],dp[7800];
bool ok(int *a,int i)
{
int tt[5] = {0};
int tmp = state[i],j = 0;
while(tmp)
{
tt[j++] = tmp % 6;
tmp /= 6;
}
for(int k = 0; k < j; k++)
{
if(tt[k] > a[k])
return false;
}
return true;
}
int main()
{
int n;
cin>>n;
con[0] = 1;
for(int i = 1; i < 5; i++)
con[i] = con[i - 1] * 6;
int sta = 0;
for(int i = 0; i < n; i++)
{
int c,k,p;
scanf("%d%d%d",&c,&k,&p);
ma[c] = i;
pri[i] = p;
sta += con[i] * k;
}
int s;
cin>>s;
for(int i = 1; i <= s; i++)
{
int k,t = 0;
scanf("%d",&k);
for(int j = 1 ;j <= k; j++)
{
int b,c;
scanf("%d%d",&b,&c);
t += con[ma[b]] * c;
}
state[i] = t;
scanf("%d",&sp[i]);
}
memset(dp,INF,sizeof(dp));
dp[0] = 0;
for(int i = 1; i <= sta; i++)
{
int t[5] = {0},tt = i,j = 0;
while(tt)
{
t[j++] = tt % 6;
tt /= 6;
}
for(int p = 0; p < j; p++)
for(int q = 1; q <= t[p]; q++)
dp[i] = min(dp[i - con[p] * q] + pri[p] * q,dp[i]);
for(int j = 1; j <= s; j++)
{
if(i >= state[j] && ok(t,j))
dp[i] = min(dp[i - state[j]] + sp[j],dp[i]);
}
}
printf("%d\n",dp[sta]);
return 0;
}