Help Bob
Problem Description
Bob loves Pizza but is always out of money. One day he reads in the newspapers that his favorite pizza restaurant, Alfredo’s Pizza Restaurant, is running a competition: they will donate a big pizza to the first person who will tell them the lowest price per area that can be achieved by buying any of the pizzas at most once. “That task is easy!”, thinks Bob, “For each pizza I just calculate the average price and the lowest quotient will be the answer.”.
Unfortunately the problem is a bit more complicated: with some pizzas Alberto gives out discount coupons for getting another pizza cheaper and even worse, those coupons can be combined. The pizzas have to be bought one after the other, and it is not possible to use a coupon to get a discount retrospectively for a pizza which has already been bought. Can you help Bob to become the first to solve this task and to get a pizza for free?
Input
The input file contains several test cases. Each test case starts with a number m, the number of pizzas Alfredo offers. Input is terminated by m=0. Otherwise, 1 ≤ m ≤ 15. Then follow m lines describing the pizzas. Each of those following lines describes pizza i (1 ≤ i ≤ m) and starts with 3 integer numbers pi, ai and ni specifying the price of the pizza, its area and the number of discount coupons you get when buying it, 1 ≤ pi ≤ 10000, 1 ≤ ai ≤ 10000 and 0 ≤ ni < m. Then follow ni pairs of integer numbers xi,j and yi,j specifying the index xi,j (1 ≤ xi,j ≤ m, xi,j ≠ i) of the pizza you get a discount coupon for and the discount in percentage terms yi,j (1 ≤ yi,j ≤ 50) you get when buying pizza xi,j. You may assume that for each i the values xi,j are pairwise distinct.
Output
For each test case print one line containing the lowest price per area that can be achieved by buying any of the pizzas at most once. Round this number to 4 places after the decimal point. Note that you can combine an arbitrary number of discount coupons: for a pizza with price 10 and two rabatt coupons for that pizza with a 50 and a 20 on it, you would only have to pay 10 * 0.8 * 0.5 = 4 monetary units.
Sample Input
1
80 30 0
2
200 100 1 2 50
200 100 0
5
100 100 2 3 50 2 50
100 100 1 4 50
100 100 1 2 40
600 600 1 5 10
1000 10 1 1 50
0
Sample Output
2.6667
1.5000
0.5333
题意:Bob喜欢比萨饼,但总是缺钱。有一天,他在报纸上看到,他最喜欢的披萨餐厅,阿尔弗雷多披萨餐厅,正在举办一场比赛:他们将向第一个人捐赠一个大披萨,第一个人会告诉他们每个地区的最低价格,这可以通过最多一次购买任何一个披萨来实现。那任务很容易!”鲍伯认为,“对于每一个比萨饼,我只计算平均价格,最低的商数就是答案。”
不幸的是,问题要复杂一些:对于一些比萨,Alberto会推出折扣优惠券,让其他比萨更便宜,甚至更糟,这些优惠券可以组合。这些比萨必须一个接一个地买,而且对于已经买的比萨,不可能使用优惠券来追溯性地获得折扣。你能帮助鲍伯成为第一个解决这个问题并且免费得到比萨饼的人吗?
题解:动态规划,状压DP
思路:由于有优惠劵,而且优惠劵可以叠加,所以与购买顺序有关,而且由于比萨饼的数量只有15,则可以考虑使用二进制数表示状态,再进行增加比萨饼进而修改为其他状态,进而求的购买i个比萨饼的最小花费
AC代码如下,代码中有详细注释
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N=1<<15+5;
const double INF=100000000;
double dp[N]; //状态为i时的最小花费
double q[20][20]; //先买i再买j的价格比例
double p[20]; //第i个产品的价格
double a[20]; //第i个产品的面积
int main(){
int n;
while(cin>>n,n){
for(int i=0;i<n;i++) //初始化赋值为全价
for(int j=0;j<n;j++)
q[i][j]=1.0;
for(int i=0;i<(1<<n);i++) //每个状态赋值为最大值
dp[i]=INF;
for(int i=0;i<n;i++){
int num;
cin>>p[i]>>a[i]>>num;
for(int j=0;j<num;j++){
int x;
double y;
cin>>x>>y;
q[i][x-1]=q[i][x-1]*(100-y)*0.01; //优惠劵可以叠加
}
}
double ans=INF;
dp[0]=0;
for(int i=0;i<(1<<n);i++){
if(dp[0]==INF) continue; //没有意义
double are=0.0;
for(int j=0;j<n;j++){
if(i&(1<<j)) are+=a[j]; //当前已经买的面积数量
else{
double cos=p[j]; //购买第j个蛋糕的价格
for(int k=0;k<n;k++){
if(i&(1<<k)) cos*=q[k][j]; //计算折扣后的价格
}
int sta=i|(1<<j); //将i的第j位置1,即购买第j个蛋糕的状态
dp[sta]=min(dp[sta],dp[i]+cos); //求的最小值
}
}
if(i) //只要买蛋糕就可以计算比例
ans=min(ans,dp[i]/are);
}
printf("%.4lf\n",ans);
}
return 0;}