hdu 2375 Help Bob(状压DP->优惠卷的搭配问题)

Help Bob
Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

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 p  i, a  i and n  i specifying the price of the pizza, its area and the number of discount coupons you get when buying it, 1 ≤ p  i ≤ 10000, 1 ≤ a  i ≤ 10000 and 0 ≤ n  i < m. Then follow ni pairs of integer numbers x  i,j and y  i,j specifying the index xi,j (1 ≤ x  i,j ≤ m, x  i,j ≠ i) of the pizza you get a discount coupon for and the discount in percentage terms y  i,j (1 ≤ y  i,j ≤ 50) you get when buying pizza x  i,j. You may assume that for each i the values x  i,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

题意很简单,求买的pizza的总价钱除于总面积的最小值,因为面积固定而价钱可以通过过程调整,很明显是状压DP的套路但是在数据的存储上确实需要很大技巧,,,

因为买了一个pizza就可以把它的优惠卷直接转换成比例累积存储下来,如果还按数量去存储的话,很会很麻烦,只要存储没问题,就只是类似于TSP的用出现过的去更新没出现过的并保留最小值得套路了


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
const int N = (1<<15)+10;
double dp[N], q[N][20], p[N], a[N];


int main()
{
    int n, num;
    while(scanf("%d", &n),n)
    {
        for(int i=0;i<(1<<n);i++)
        {
            dp[i]=100000000;
            for(int j=0;j<=n;j++)
            {
                q[i][j]=1.0;
            }
        }
        for(int i=0;i<n;i++)
        {
            scanf("%lf %lf",&p[i], &a[i]);
            scanf("%d", &num);
            for(int j=1;j<=num;j++)
            {
                int x;
                double y;
                scanf("%d %lf",&x, &y);
                q[i][x-1]=q[i][x-1]*(100-y)*0.01;
            }
        }
        double ans=100000000;
        dp[0]=0;
        for(int i=0;i<(1<<n);i++)
        {
            if(dp[0]==100000000)
                continue;
            double area=0;
            for(int j=0;j<n;j++)
            {
                if(i&(1<<j))
                    area+=a[j];
                else
                {
                    double cost=p[j];
                    for(int k=0;k<n;k++)
                    {
                        if(i&(1<<k))
                            cost*=q[k][j];
                    }
                    int state=i|(1<<j);
                    if(dp[state]>dp[i]+cost)
                        dp[state]=dp[i]+cost;
                }
            }
            if(i)
                ans=min(ans,dp[i]/area);
        }
        printf("%.4f\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值