题目链接
题面
题目背景
在 2020 年的某一天,我们的 yyy2015c01 买了个高端 U 盘。
题目描述
你找 yyy2015c01 借到了这个高端的 U 盘,拷贝一些重要资料,但是你发现这个 U 盘有一些问题:
- 这个 U 盘的传输接口很小,只能传输大小不超过 L L L 的文件。
- 这个 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
1≤n,Wi,S≤103,
1
≤
V
i
≤
1
0
6
1 \leq V_i \leq 10^6
1≤Vi≤106,
1
≤
p
≤
1
0
9
1 \leq p \leq 10^9
1≤p≤109。
数据较小,请勿乱搞。
样例解释
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[j−w[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背包题目,要善于将所学知识结合起来使用