洛谷P2370 yyy2015c01 的 U 盘【贪心 / 二分+01背包DP】【黄】

Date:2022.03.13
题目描述
你找 yyy2015c01 借到了这个高端的 U 盘,拷贝一些重要资料,但是你发现这个 U 盘有一些问题:
这个 U 盘的传输接口很小,只能传输大小不超过 LL 的文件。
这个 U 盘容量很小,一共只能装不超过 SS 的文件。
但是你要备份的资料却有很多,你只能备份其中的一部分。
为了选择要备份哪些文件,你给所有文件设置了一个价值 V i V_i Vi,你希望备份的文件总价值不小于 p。
但是很快你发现这是不可能的,因为 yyy2015c01 的传输接口太小了,你只有花钱买一个更大的接口(更大的接口意味着可以传输更大的文件,但是购买它会花费更多的钱)。
注意:你的文件不能被分割(你只能把一个文件整个的传输进去,并储存在U盘中),
你放在 U 盘中文件的总大小不能超过 U 盘容量。
现在问题来了:你想知道,在满足 U 盘中文件价值之和不小于 p 时,最小需要多大的接口。
输入格式
第 1 行,三个正整数 n,p,S 分别表示文件总数,希望最小价值 p ,硬盘大小。
接下来 n 行,每行两个正整数 W i 、 V i W_i、V_i WiVi,表示第 i 个文件的大小和价值。
输出格式
输出一个正整数表示最小需要的接口大小。
如果无解输出 No Solution!。
输入输出样例
输入 #1复制
3 3 5
2 2
1 2
3 2
输出 #1复制
2
输入 #2复制
2 3 505
1 2
500 1
输出 #2复制
500
输入 #3复制
3 3 2
2 2
1 2
3 2
输出 #3复制
No Solution!
输入 #4复制
4 5 6
5 1
5 2
5 3
1 1
输出 #4复制
No Solution!
说明/提示
数据较小,请勿乱搞。
样例解释 1:买一个大小为 2 接口,把物品 1 、2 放进U盘。
样例解释 2:买一个大小为 500 的接口。
样例解释 3:本来可以买大小为 2 的接口,可是 U 盘容量放不下足够的文件。

题意有点拉,解释大意:U盘体积最多装S(文件体积不能超过S);每个文件有Wi(体积)、Vi(价值),要求的是U盘中所有文件价值>=p时,最少要多大的接口?即使得这个状态下所有文件都能由这个大小为L的接口传送(即所有文件大小<=L)。
思路①:01背包,费用为体积。由上L显然是某种合法状态下所有文件中的最大值,而且要L尽可能小,因此考虑二分。
二分什么?
答:每次 c h e c k ( m i d ) check(mid) check(mid),按照01背包的过程,若在L的大小为mid时使得所有体积<=mid的物品选出的最大价值>=p,则mid可能为答案,二分 [ l , m i d ] [l,mid] [l,mid](mid可变小,从而使 f [ S ] 更 小 f[S]更小 f[S]);反之mid必然小了,不可能为答案,二分 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]。经此,二分所得答案即为在保证累加所有文件的最大价值>=p下的最小L。(好妙!)
代码如下:

#include 
using namespace std;
const int N = 1010;
typedef long long LL;
LL n,m,p,k,s,f[N],v[N],w[N];
bool check(LL mid)
{
    memset(f,0,sizeof f);
    for(int i=1;i<=n;i++)
    {
        if(w[i]>mid) continue;
        for(int j=s;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    }
    if(f[s]>=p) return true;
    return false;
}
int main()
{
    cin>>n>>p>>s;
    for(int i=1;i<=n;i++) cin>>w[i]>>v[i];
    LL l=0,r=s+1;//避免边界(不然wa一个点)
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    if(l<=0||l>s) cout<<"No Solution!";
    else cout<<l;
    return 0;
}

思路②:贪心。排序后费用由小到大,01背包,找到第一个 f [ S ] > = p f[S]>=p f[S]>=p,至此一定是选择了当前的元素,否则不会>=p,因此刚选的文件体积必定是最大的L。
代码如下:

#include 
using namespace std;
const int N = 1010;
typedef long long LL;
LL n,m,p,k,S,f[N];
struct node {LL v,w;}s[N];
bool cmp(node a,node b) {return a.w<b.w;}
int main()
{
    cin>>n>>p>>S;
    for(int i=1;i<=n;i++) cin>>s[i].w>>s[i].v;
    sort(s+1,s+1+n,cmp);
    for(int i=1;i<=n;i++)
        for(int j=S;j>=s[i].w;j--)
        {
            f[j]=max(f[j],f[j-s[i].w]+s[i].v);
            if(f[j]>=p) {cout<<s[i].w;return 0;}
        }
    cout<<"No Solution!";
    return 0;
}

你可能感兴趣的:(dp,思维与基础,动态规划,c++,算法)