P2370 yyy2015c01 的 U 盘

题目链接

Luogu P2370

题面

题目背景

在 2020 年的某一天,我们的 yyy2015c01 买了个高端 U 盘。

题目描述

你找 yyy2015c01 借到了这个高端的 U 盘,拷贝一些重要资料,但是你发现这个 U 盘有一些问题:

  1. 这个 U 盘的传输接口很小,只能传输大小不超过 L L L 的文件。
  2. 这个 U 盘容量很小,一共只能装不超过 S S S 的文件。但是你要备份的资料却有很多,你只能备份其中的一部分。为了选择要备份哪些文件,你给所有文件设置了一个价值 V i V_i Vi,你希望备份的文件总价值不小于 p p p
    但是很快你发现这是不可能的,因为 yyy2015c01 的传输接口太小了,你只有花钱买一个更大的接口(更大的接口意味着可以传输更大的文件,但是购买它会花费更多的钱)。
    注意:你的文件不能被分割(你只能把一个文件整个的传输进去,并储存在U盘中),
    你放在 U 盘中文件的总大小不能超过 U 盘容量。
    现在问题来了:你想知道,在满足 U 盘中文件价值之和不小于 p p p 时,最小需要多大的接口。

输入格式

1 1 1 行,三个正整数 n , p , S n,p,S n,p,S 分别表示文件总数,希望最小价值 p p p ,U 盘大小。
接下来 n n n 行,每行两个正整数 W i , V i W_{i},V_{i} Wi,Vi,表示第 i i i 个文件的大小和价值。

输出格式

输出一个正整数表示最小需要的接口大小。
如果无解输出 No Solution!

样例 #1

样例输入 #1

3 3 5
2 2
1 2
3 2

样例输出 #1

2

样例 #2

样例输入 #2

2 3 505
1 2
500 1

样例输出 #2

500

样例 #3

样例输入 #3

3 3 2
2 2
1 2
3 2

样例输出 #3

No Solution!

样例 #4

样例输入 #4

4 5 6
5 1
5 2
5 3
1 1

样例输出 #4

No Solution!

提示

1 ≤ n , W i , S ≤ 1 0 3 1 \le n, W_i, S \le 10^3 1n,Wi,S103 1 ≤ V i ≤ 1 0 6 1 \leq V_i \leq 10^6 1Vi106 1 ≤ p ≤ 1 0 9 1 \leq p \leq 10^9 1p109
数据较小,请勿乱搞。
样例解释 1 1 1:买一个大小为 2 2 2 接口,把物品 1 1 1 2 2 2 放进 U \text{U} U盘。
样例解释 2 2 2:买一个大小为 500 500 500 的接口。
样例解释 3 3 3:本来可以买大小为 2 2 2 的接口,可是 U 盘容量放不下足够的文件。
如果数据出现疏漏,请联系出题人 a710128
向本题主人公 yyy2015c01 同学致敬!

题意

n n n 个文件,每个文件有大小 w w w价值 v v v 两个变量。
现将其中一些文件存储到一个容量为 S S S ,传输接口大小为 L L L(若文件大小大于U盘的传输接口大小,则此文件无法传入U盘)的U盘中,使得存入U盘的文件价值总和不小于 p p p ,求 L L L 的最小值

思路

若此题去除 L L L 这个元素,则与 01 01 01背包的模板无异
很容易的到此题的状态转移方程

d p [ j ] = m a x ( d p [ j ] , d p [ j − w [ i ] ] + v [ i ] ) dp[j]=max(dp[j],dp[j-w[i]]+v[i]) dp[j]=max(dp[j],dp[jw[i]]+v[i])

L L L最小时,必然是 n n n 个文件的大小之一,所以我们以文件作为阶段
然后以文件大小进行分类讨论,最后再套上状态转移方程
易得 d p [ i ] dp[i] dp[i] 代表U盘容量为 i i i 时所存储的最大文件价值之和
那如何求得 L L L 呢?
我们先对 w [ i ] w[i] w[i] 顺序排序,然后每次更新 d p dp dp 数组时判断当前的 d p [ j ] dp[j] dp[j] 是否达标(即 d p [ j ] ≥ p dp[j]\geq p dp[j]p
若当前 d p [ j ] dp[j] dp[j]达标,则 L L L 为 当前的 w [ i ] w[i] w[i]
因为 d p dp dp 前已经对 w w w 数组顺序排序,所以当前的 w [ i ] w[i] w[i] 就是答案
AC code

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct Node{
	int w,v;
}node[1111];
int n,p,S,dp[1111];
bool cmp(Node x,Node y){
	return x.w<y.w;
}
signed main(){
	scanf("%lld%lld%lld",&n,&p,&S);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&node[i].w,&node[i].v);
	sort(node+1,node+1+n,cmp);//对w[i]顺序排序
	for(int i=1;i<=n;i++){
		for(int j=S;j>=node[i].w;j--){
			dp[j]=max(dp[j],dp[j-node[i].w]+node[i].v);//01背包——状态转移方程
			if(dp[j]>=p){//dp[j]达标
				printf("%lld",node[i].w);//输出答案
				return 0;
			}
		}
	}
	printf("No Solution!");//不满足的情况
	return 0;
}

此代码时间复杂度为 O ( n S ) O(nS) O(nS)


其实这题还能用二分答案+dp来写(其实这才是正解( ̄▽ ̄)
思路很简单,用二分枚举求 L L L c h e c k check check 函数用 d p dp dp 来验证可行性
具体见代码注释

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct Node{
	int w,v;
}node[1111];
int n,p,S,dp[1111];
bool check(int x){//二分答案check函数
	memset(dp,0,sizeof dp);
	for(int i=1;i<=n;i++){
		if(node[i].w>x)continue;//越界
		for(int j=S;j>=node[i].w;j--){
			dp[j]=max(dp[j],dp[j-node[i].w]+node[i].v);//01背包——状态转移方程
		}
	}
	return dp[S]>=p;//返回是否达标
}
int l,r,mid;
bool flag=true;
signed main(){
	scanf("%lld%lld%lld",&n,&p,&S);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&node[i].w,&node[i].v),r=max(r,node[i].w);
	while(l+1<r){//二分答案
		mid=(l+r)>>1;
		if(check(mid))r=mid,flag=false;
		else l=mid;
	}
	if(flag)printf("No Solution!");//不满足的情况
	else printf("%lld",r);
	return 0;
}

总结

这是一道很普通的 01 01 01背包题目,要善于将所学知识结合起来使用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值