码迷,mamicode.com
首页 > 其他好文 > 详细

CF730J 【Bottles】

时间:2018-03-24 21:26:29      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:决定   打水   cti   就是   背包   std   ams   UI   ring   

先谈谈看到这题难度时的表现吧

什么鬼!!!又是NOI难度的

却发现做了还是挺简单的

首先来看看题意(本人英语不好,先有道翻译一次再改了语法)

尼克过生日后留下了n瓶苏打水。每个瓶子由两个值来描述:剩余量的苏打水。我以及瓶子容量b{i} b。我(现代{我} < = b {我}我< = b我)。尼克已经决定把所有剩余的汽水倒入最少的瓶子里,而且他必须尽快把它做完。尼克花了x秒的时间从一个瓶子里倒出x个单位的苏打水。Nick请您帮他确定kk——储存所有剩余的苏打水和tt的最小瓶数——将苏打水倒入k k瓶的最小时间。瓶子不能储存比体积更多的苏打水。所有剩余的苏打水都应该保存。

好,什么也没有发生

看一下中文正宗版

现在有n个瓶子,从里面选出x个,使得这x个瓶子的容积之和要大于所有水的体积,并且还要使得在时间最短前提下,要使这些瓶子里的水体积和最大

于是你会神奇的发现这道题好像跟以往贪心或dp的题有点像了

贪心的话:按容量sort一遍,然后......

不管了直接来想dp,如果把水的体积类比到背包容积,将瓶子内的水类比成物品重量,把所需时间类比到价值

你会发现是一个背包问题

又因为只能取一次,所以是01背包

我们来想状态转移方程

令f[i][j][k]表示处理到了第i个瓶子,已经选了j个瓶子,容量为k的最大已有水量。 状态转移方程: f[i][j][k]=max(f[i][j][k],f[i?1][j?1][k?b[i]]+a[i])

然后就又可以类比到01背包去掉一位

证明就是每一次递推只会被i-1或i的答案覆盖,无论从哪种情况都不会用其他解覆盖最优解

于是就可以写了

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,xy,sum,ans;
int f[105][10005],a[105],b[105];
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        xy+=a[i];
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        sum+=b[i];
    }
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    for (int i=1;i<=n;i++)
        for (int j=n;j>=1;j--)
            for (int k=sum;k>=b[i];k--)
                if (f[j-1][k-b[i]]!=-1)
                    f[j][k]=max(f[j][k],f[j-1][k-b[i]]+a[i]);
    ans=-1;
    for (int j=1;j<=n;j++)
    {
        for (int k=xy;k<=sum;k++)
            ans=max(ans,f[j][k]);
        if (ans!=-1)
        {
            printf("%d %d",j,xy-ans);
            break;
        }
    }
    return 0;
}

CF730J 【Bottles】

标签:决定   打水   cti   就是   背包   std   ams   UI   ring   

原文地址:https://www.cnblogs.com/66t6/p/8641353.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!