先谈谈看到这题难度时的表现吧
什么鬼!!!又是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;
}