U - Inviting Friends HDU - 3244(二分答案 + 完全背包)

U - Inviting Friends HDU - 3244

You want to hold a birthday party, inviting as many friends as
possible, but you have to prepare enough food for them. For each
person, you need n kinds of ingredient to make good food. You can use
the ingredients in your kitchen, or buy some new ingredient packages.
There are exactly two kinds of packages for each kind of ingredient:
small and large.

We use 6 integers to describe each ingredient: x, y, s1, p1, s2, p2,
where x is the amount (of this ingredient) needed for one person, y is
the amount currently available in the kitchen, s1 and p1 are the size
(the amount of this ingredient in each package) and price of small
packages, s2 and p2 are the size and price of large packages.

Given the amount of money you can spend, your task is to find the
largest number of person who can serve. Note that you cannot buy only
part of a package. Input There are at most 10 test cases. Each case
begins with two integers n and m (1<=n<=100, 1<=m<=100000), the number
of kinds of ingredient, and the amount of money you have. Each of the
following n lines contains 6 positive integers x, y, s1, p1, s2, p2 to
describe one kind of ingredient (10<=x<=100, 1<=y<=100, 1<=s1<=100,
10<=p1<=100, s1<s2<=100, p1<p2<=100). The input ends with n = m = 0.
Output For each test case, print the maximal number of people you can
serve. Sample Input

2 100
10 8 10 10 13 11
12 20 6 10 17 24
3 65
10 5 7 10 13 14
10 5 8 11 14 15
10 5 9 12 15 16
0 0
Sample Output
5
2

思路

  • 这一题挺不错的,把 二分的知识 与 背包问题的知识放在一起来考察我们,这一题我们要二分的是 招待的 friend 的数量, 而我们一但二分答案假设出一个 朋友数量,我们就需要判断 用厨房里剩余的菜 + 手里的钱💰买的菜 够不够 这些朋友 所需的数量, 而买菜的是时候,由于每种菜 大袋、小袋,两种尺寸,而切 大袋小袋袋 价格也不同,没有一种固定的方法 来解绝怎么买 会,所以可从 dp 方面思考,,,对于某一种菜,假设我们需要的数量是 被背包的空间,大袋、小袋的尺寸是 物品的空间, 而大袋、小袋的价格就是 物品的价值,那么我们这个 ,又因为 物品可以被买多次,所以 是多重背包问题,显然我们要求的是背包 中装的最小价值(所以在出事化的时候我们要注意:出来 d p [ 0 ] , 其 他 的 全 部 初 始 化 为 I N F dp[0],其他的全部初始化为 INF dp[0],INF),这个价值就是最小花费,,,,,这样整个题就解决了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<string>
using namespace std;

#define ll long long 
#define db double 
#define INF 1e8
const int mod = 1e9+7;
const int maxn = 105;
int n, m;
int dp[1000005];

struct Food
{
    int x, y, s1, p1, s2, p2;
} food[maxn];

int Cal_r()
{
    int ans = INF;
    for(int i = 1; i <= n; i ++)
        ans = (min(m / food[i].p1 * food[i].s1, m / food[i].p2 * food[i].s2) - food[i].y) / food[i].x;
    return ans + 5;
}

int Cal_mon(int i, int num)
{
    int need = food[i].x * num - food[i].y;
    if(need <= 0) return 0;
    need += food[i].s2;

    for(int i = 0; i <= need; i ++)
        dp[i] = INF;
    dp[0] = 0;
    int w,v;        //v 是该物品所占的空间、w就是该物品的价值
    for(int j = 1; j <= 2; j ++)
    {
        if(j == 1)
            w = food[i].p1, v = food[i].s1;
        else
            w = food[i].p2, v = food[i].s2;
        for(int k = v; k <= need; k ++)
            dp[k] = min(dp[k], dp[k - v] + w);
    }
    int mn = INF;
    for(int j = need - food[i].s2; j <= need; j ++)
    {
        mn = min(mn, dp[j]);
    }
    /* cout << mn << endl; */
    return mn;
}


bool Solve(int num)
{
    int mon = m;
    for(int i = 1; i <= n; i ++)
    {
        mon -= Cal_mon(i, num);
        if(mon < 0) return false;
    }
    return true;
}


int main()
{
    /* freopen("A.txt","r",stdin); */
    /* freopen("Ans.txt","w",stdout); */
    while(scanf("%d %d", &n, &m) && n)
    {
        int l = 0, r;
        for(int i = 1; i <= n; i ++)
            scanf("%d %d %d %d %d %d", &food[i].x, &food[i].y, &food[i].s1, &food[i].p1, &food[i].s2, &food[i].p2);
        r = Cal_r();
        int mid, ans;
        while(l <= r)
        {
           mid = (l + r) >> 1; 
           if(Solve(mid))
               l = mid + 1, ans = mid;
           else
               r = mid - 1;
        }
        printf("%d\n", ans);
    }

    return 0;
}
  • 总结
  1. 这一遗憾的就没想到,买某种菜的时候要 用多重的背包的思想,,,,以后做题要仔细 分析每一步分 用到的东西是什么
  2. 对与这种 给我们一定的钱数,让我们估算最多买东西的数量时,我们可以用二分来解决
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值