1. 程式人生 > >[洛谷]P2370 yyy2015c01的U盤 (#二分答案 -1.1)(#動態規劃 -揹包 -1.11)

[洛谷]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;
}