甜点(多重背包+二进制优化)

描述

小z准备举办一个比赛。他需要提供一些甜点给参赛者来补充能量。每种甜品有一定的能量ti和大小ui,且每种甜点最多有vi个。

小z准备用箱子来包装甜点。箱子可以容纳一定体积的甜点且需要一定的费用。小z有一种魔法,可以将一个甜点分成多份装在箱子里,最后再合在一起(但合成之后必须是完整的一个)。

小z想知道准备能量至少为P的甜点的最小大小和最少需要多少费用来购买箱子,如果最少费用超过小z所拥有的钱数k则输出FAIL。

输入

第一行为4个正整数n,m,p, k( 1 ≤ n ≤ 200,1 ≤ m ≤ 200,0 ≤ p ≤ 50000, k <= 50000)分别代表甜点种类,箱子种类和参赛者比赛所需要补充的能量和小z所拥有的钱数。 接下来的n行,每行包含3个整数ti, ui, vi ( 1 ≤ ti ≤ 100,1 ≤ ui ≤ 100,1 ≤ vi ≤ 100) , 代表第i类甜点可以提供ti的能量,它的大小为ui并且小明最多有vi个该种类的甜点。 接下来又有m行,每一行包含3个整数xi, yi, zi ( 1 ≤ xj ≤ 100,1 ≤ yj ≤ 100,1 ≤ zj ≤ 100), 代表第i类箱子可以容纳xi大小的甜点,该类箱子的单价yi,并且小z最多可以使用zi个该类的箱子。

输出

第一行请输出最小的甜点大小。 第二行请输出最小的箱子费用,并且费用不能超过k。否则,输出FAIL。

样例输入1

5 3 34 34
1 4 1
9 4 2
5 3 3
1 3 3
5 3 2
3 4 5
6 7 5
5 3 8

样例输出1

19
12

思路:我一直觉得这题有毒,比赛的时候再想那句“小z有一种魔法,可以将一个甜点分成多份装在箱子里,最后再合在一起(但合成之后必须是完整的一个)。”,到底是什么意思??,因为多重背包来做的话,选择放或不放,可能不能将包装满,但是它说可以把甜点拆分,那我觉得还背包啥啊,第一步选最小体积背包做,第二步贪心做就行。结果......

后来我想明白了,它加那句话就是让你第二步也强行使用背包,因为这样不需要考虑一开始选了什么大小的甜品,直接考虑总重量就行。(但感觉题目还是有点问题,大概是我太菜了)

所以:第一步,求最小体积,逆向来做,设dp[i]表示体积为i的甜品,总能量最大。求完以后,从1到maxn遍历一下,大于P就挑出即可。设为ans1.

第二步:求最小费用,dp2[i]表示费用为i时,获得的最大体积,同样的从1到k遍历一遍,找到dp2[i]>ans1即可退出。

 

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
int n,m,p,k;
int t[maxn],u[maxn],numv[maxn];
int vt[maxn],vu[maxn];
int vx[maxn],vy[maxn],vz[maxn];
int dp[maxn];
struct Point
{
    int x,y,z;
}pp[maxn];
bool cmp(Point a,Point b)
{
    return a.y*b.x<a.x*b.y;
}
bool vis[500];
int dp2[maxn];
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    scanf("%d%d%d%d",&n,&m,&p,&k);
    int cnt=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&t[i],&u[i],&numv[i]);
        for(int j=1;j<=numv[i];j*=2)
        {
            vt[cnt]=t[i]*j;
            vu[cnt++]=u[i]*j;
            numv[i]-=j;
        }
        if(numv[i])
        {
            vt[cnt]=t[i]*numv[i];
            vu[cnt++]=u[i]*numv[i];
        }
    }
    //memset(dp,127,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<cnt;i++)
    {
        for(int j=100000;j>=vu[i];j--)
        {
            dp[j]=max(dp[j],dp[j-vu[i]]+vt[i]);
        }
    }
    cnt=1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&pp[i].x,&pp[i].y,&pp[i].z);
        for(int j=1;j<=pp[i].z;j*=2)
        {
            vx[cnt]=pp[i].x*j;
            vy[cnt++]=pp[i].y*j;
            pp[i].z-=j;
        }
        if(pp[i].z>0)
        {
            vx[cnt]=pp[i].x*pp[i].z;
            vy[cnt++]=pp[i].y*pp[i].z;
        }
    }
    dp2[0]=0;
    for(int i=1;i<cnt;i++)
    {
        for(int j=100000;j>=vy[i];j--)
        {
            dp2[j]=max(dp2[j],dp2[j-vy[i]]+vx[i]);
        }
    }
    //printf("%d\n",dp2[12]);
    int ans1=-1,ans2=-1;
    for(int i=1;i<=100000;i++)
    {
        if(dp[i]>=p)
        {
            ans1=i;
            break;
        }
    }
    if(dp[k]<ans1)
    {
        printf("FAIL\n");
    }
    else
    {
        printf("%d\n",ans1);
        for(int i=1;i<=k;i++)
        {
            if(dp2[i]>=ans1)
            {
                ans2=i;
                break;
            }
        }
        printf("%d\n",ans2);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值