背包 CF730J Bottles 题解
【动态规划 背包】CF730J Bottles
前言
此乃小 Oler 的一篇小小题解,从今日后,还会进行详细的修订。
6870ef4f-02f8-4dc3-a0b2-4ccc37f75eab
Bottles
题目
题目描述
有 n n n 瓶水,第 i i i 瓶水的水量为 a i a_i ai,容量为 b i b_i bi。将 1 1 1 单位水从一个瓶子转移到另一个瓶子所消耗时间为 1 1 1 秒,且可以进行无限次转移。求储存所有水所需最小瓶子数 k k k 以及该情况下所用最小时间 t t t 。
输入格式
第一行输入一个正整数 n n n( 1 ≤ n ≤ 100 1\le n\le 100 1≤n≤100)。
第二行输入 n n n 个正整数,第 i i i 个正整数表示 a i a_i ai( 1 ≤ a i ≤ 100 1\le a_i \le 100 1≤ai≤100)。
第三行输入 n n n 个正整数,第 i i i 个正整数表示 b i b_i bi( 1 ≤ b i ≤ 100 1\le b_i \le100 1≤bi≤100)。
对于每一个 i i i,满足 a i ≤ b i a_i\le b_i ai≤bi 。
输出格式
输出一行两个整数: k k k 和 t t t 。
样例 #1
样例输入 #1
4
3 3 4 3
4 7 6 5
样例输出 #1
2 6
样例 #2
样例输入 #2
2
1 1
100 100
样例输出 #2
1 1
样例 #3
样例输入 #3
5
10 30 5 6 24
10 41 7 8 24
样例输出 #3
3 11
提示
在第一个例子中, Nick 可以将苏打水从第一个瓶子倒到第二个瓶子。这需要 3 3 3 秒钟。在它之后,第二瓶将包含 3 + 3 = 6 3+3=6 3+3=6 单位的苏打水。然后他可以把苏打水从第四瓶倒到第二瓶,再倒到第三瓶:一个单位到第二个,两个单位到三个。这将花费 1 + 2 = 3 1+2=3 1+2=3 秒。所以,所有的苏打水都装在两个瓶子里,他会花 3 + 3 = 6 3+3=6 3+3=6 秒来做。
题解
题意
每个水瓶有两个值,一个是初始时水瓶中的水量 v a l val val ,一个是水瓶的容量 v v v ,求通过转移,储存所有水所需最小瓶子数 k k k 以及该情况下转移所用最小时间 t t t 。
流程
I. 最小瓶子数(简单贪心)
-
- 求所有水瓶中的水量之和 s u m sum sum
-
- 把每个水瓶按容量从大到小排序
-
- 依次取出容量大的水瓶,直到取到大于 s u m sum sum 为止
-
- 把最小瓶子数存入 r e s res res 中
II. 最小时间
- 定义 f i , j , k f_{i,j,k} fi,j,k 为考虑前 i i i 个水瓶,选了 j j j 个水瓶,容量是 k k k 时水量最大值。
f
i
,
j
,
k
=
max
{
f
i
−
1
,
j
,
k
→
不选
f
i
−
1
,
j
−
1
,
k
−
v
+
v
a
l
→
选
f_{i,j,k} = \max
由于数据比较大,如果直接开数组,会给你个惊喜的 MLE !!!
想到我们使用的是背包思想,所以可以省略第一维,即修改状态定义为 f j , k f_{j,k} fj,k ,选了 j j j 个水瓶,容量是 k k k 时水量和的最大值,但要注意在枚举选了 j j j 个水瓶的循环时需倒序进行。
由于要求的是在取最少个瓶子情况下的最优解,就是说明选几个水瓶是已经确定的了,即 r e s res res ,最后枚举容量为 i i i 时的水量最大值 f r e s , i f_{res,i} fres,i ,取其中的最大值,题目并没有要求转移水量的顺序,则可以随意转移,所以用原来所有水瓶中的水量和和减去选中的水瓶中的水量和(需要转移的水量),就是最小时间。
下面贴上蒟蒻的代码…
Code ( AC , 100ps )
#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n,ans,f[N][N*N];
int sum,res,sum2;
struct Node {
int a,b;
}d[N];
bool cmp(Node x,Node y) {
return x.b>y.b;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&d[i].a);
sum+=d[i].a;
}
for(int i=1;i<=n;i++) {
scanf("%d",&d[i].b);
sum2+=d[i].b;
}
sort(d+1,d+n+1,cmp);
int t=0;
for(int i=1;i<=n;i++) {
t+=d[i].b,res++;
if(t>=sum) break;
}
memset(f,-0x3f3f3f3f,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;i++) {
for(int j=i;j>=1;j--) {
for(int k=j*100;k>=d[i].b;k--)
f[j][k]=max(f[j-1][k-d[i].b]+d[i].a,f[j][k]);
}
}
int ans=0;
for(int i=sum;i<=sum2;i++)
ans=max(ans,f[res][i]);
printf("%d %d\n",res,sum-ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现