背包 CF730J Bottles 题解


前言

此乃小 Oler 的一篇小小题解,从今日后,还会进行详细的修订

6870ef4f-02f8-4dc3-a0b2-4ccc37f75eab

Bottles

题目

源自 洛谷 CF730J 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 1n100)。

第二行输入 n n n 个正整数,第 i i i 个正整数表示 a i a_i ai 1 ≤ a i ≤ 100 1\le a_i \le 100 1ai100)。

第三行输入 n n n 个正整数,第 i i i 个正整数表示 b i b_i bi 1 ≤ b i ≤ 100 1\le b_i \le100 1bi100)。

对于每一个 i i i,满足 a i ≤ b i a_i\le b_i aibi

输出格式

输出一行两个整数: 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. 最小瓶子数(简单贪心)
    1. 求所有水瓶中的水量之和 s u m sum sum
    1. 把每个水瓶按容量从大到小排序
    1. 依次取出容量大的水瓶,直到取到大于 s u m sum sum 为止
    1. 最小瓶子数存入 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 {fi1,j,kfi1,j1,kv+val fi,j,k=max{fi1,j,k不选fi1,j1,kv+val

由于数据比较大,如果直接开数组,会给你个惊喜的 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;
}

posted @   Fireworks_Rise  阅读(25)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示