[洛谷]P2370 yyy2015c01的U盤 (#二分答案 -1.1)(#動態規劃 -揹包 -1.11)
題目背景
在2020年的某一天,我們的yyy2015c01買了個高階U盤。
題目描述
你找yyy2015c01借到了這個高階的U盤,拷貝一些重要資料,但是你發現這個U盤有一些問題:
1、這個U盤的傳輸介面很小,只能傳輸大小不超過L的檔案
2、這個U盤容量很小,一共只能裝不超過S的檔案
但是你要備份的資料卻有很多,你只能備份其中的一部分。
為了選擇要備份哪些檔案,你給所有檔案設定了一個價值Vi,你希望備份的檔案總價值不小於 p
但是很快你發現這是不可能的,因為yyy2015c01的傳輸介面太小了,你只有花錢買一個更大的介面(更大的介面意味著可以傳輸更大的檔案,但是購買它會花費更多的錢)
注意:你的檔案不能被分割(你只能把一個檔案整個的傳輸進去,並儲存在U盤中),
你放在U盤中檔案的總大小不能超過U盤容量
現在問題來了:你想知道,在滿足U盤中檔案價值之和 不小於 p時,最小需要多大的介面
輸入輸出格式
輸入格式:
第1行,三個正整數 n, p, S 分別表示 檔案總數, 希望最小价值p,硬碟大小
接下來n行
每行兩個正整數 Wi, Vi 表示 第i個檔案的大小,和價值
輸出格式:
一共1行,輸出一個正整數表示最小需要的介面大小
如果無解輸出 “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 ≤ n, Wi, S ≤ 1 000
1 ≤ Vi ≤ 1 000 000
1 ≤ p ≤ 1 000 000 000
資料較小,請勿亂搞。
樣例解釋1: (買一個大小為2介面,把物品1、2放進U盤)
樣例解釋2:(買一個大小為500的介面)
樣例解釋3:(本來可以買大小為2的介面,可是U盤容量放不下足夠的檔案)
如果資料出現疏漏,請聯絡出題人@a710128
向本題主人公yyy2015c01同學致敬
思路
先來bb兩句本題的介面是什麼:
比如一個檔案大小是4,那你至少要準備一個4大小的介面。小於4的檔案都可以使用這個藉口,但是如果檔案大小是5,就不能用4介面了,必須買一個5介面的。
然後,本題可以用二分來列舉最小的介面。根據上面對於介面的解釋,我們可以r=s,l=0,然後揹包來驗證價值能否大於最小价值。
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
int n,m,s,maxn(-1);
int dp[1001],v[1001],w[1001];
bool bag01(int cnt)//然後揹包來驗證價值能否大於最小价值
{
memset(dp,0,sizeof(dp));//清除快取
register int i,j;
for(i=1;i<=n;i++)
{
if(w[i]>cnt)//cnt是當前的mid,也就是目前的介面大小
{//如果這個檔案的大小大於介面的大小
continue;//不能裝
}
else for(j=s;j>=w[i];j--)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//裸的01揹包,dp[i]大小為i時價值最大
}
}
if(dp[s]<m)//01揹包的dp[s]不就是價值最大了嗎?!但是如果找的還沒有最小价值m(本題中是p)大
{
return 0;
}
else
{
return 1;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int i,j,l,r,mid;
cin>>n>>m>>s;
for(i=1;i<=n;i++)
{
cin>>w[i]>>v[i];//讀入大小、價值
}
l=0,r=s;//解釋過藉口是什麼意思了,那麼區間上限就是磁碟的大小
while(l<=r)//二分來列舉最小的介面,結束條件為l>r
{
mid=(l+r)>>1;//顯然第一步先找區間中點
if(bag01(mid))//bag是返回mid是否可行,
{
maxn=mid;//mid可行,此時記錄maxn=mid,mid是目前最優解
r=mid-1;//縮小查詢範圍,r=mid-1,尋找有沒有更小的解
}//之所以這樣做是因為本題也間接提到了最大的最小值
else//mid不是最優解,就往大區間找,因為我們希望越小越好,但是目前mid是最小的,且此方案行不通,那就往大區間再找
{
l=mid+1;//找右區間某一個比當前mid大的值
}
}
if(maxn==-1)
{
cout<<"No Solution!"<<endl;
}
else
{
cout<<maxn<<endl;
}
return 0;
}