1. 程式人生 > >(超詳細)POJ3104 Drying(讓我痛不欲生的二分)

(超詳細)POJ3104 Drying(讓我痛不欲生的二分)

Description

It is very hard to wash and especially to dry clothes in winter. But Jane is a very smart girl. She is not afraid of this boring process. Jane has decided to use a radiator to make drying faster. But the radiator is small, so it can hold only one thing at a time.

Jane wants to perform drying in the minimal possible time. She asked you to write a program that will calculate the minimal time for a given set of clothes.

There are n clothes Jane has just washed. Each of them took ai water during washing. Every minute the amount of water contained in each thing decreases by one (of course, only if the thing is not completely dry yet). When amount of water contained becomes zero the cloth becomes dry and is ready to be packed.

Every minute Jane can select one thing to dry on the radiator. The radiator is very hot, so the amount of water in this thing decreases by k this minute (but not less than zero — if the thing contains less than k water, the resulting amount of water will be zero).

The task is to minimize the total time of drying by means of using the radiator effectively. The drying process ends when all the clothes are dry.

Input

The first line contains a single integer n (1 ≤ n ≤ 100 000). The second line contains ai separated by spaces (1 ≤ ai ≤ 109). The third line contains k (1 ≤ k ≤ 109).

Output

Output a single integer — the minimal possible number of minutes required to dry all clothes.

Sample Input

sample input #1
3
2 3 9
5

sample input #2
3
2 3 6
5

Sample Output

sample output #1
3

sample output #2
2

 題意:n件衣服要烘乾,第i件衣服的水分為a[i],烘乾機每次只能放進一件衣服,每分鐘能烘乾的水分為k,同時每分鐘沒有放在洗衣機的衣服的水分會自然蒸發1,求最短需要多少時間把衣服全部烘乾。

思路:拿到這個題,想到用二分來做不難,難得是如何寫判斷的函式。。。(吐血。。)

首先理順一下題目,我們二分的是題目所求也就是最少用的時間,寫到二分裡就是mid。那麼我們如何判斷mid是否符合要求來縮小範圍呢?

判斷函式的寫法是這個題的關鍵,

先來想想簡單的部分:如果所有的a[i]都小於mid,那麼肯定是可行的,都不需要用洗衣機,放在那風乾時間也不會超過mid。

 但是如果有的a[i]大於mid呢?我們知道如果這些衣服都不放洗衣機的話,也會自動減少水分,這是自然發生的,不管你有沒有用洗衣機,然後設給i件衣服用了t時間的洗衣機,那麼mid時間後,自然風乾後剩下的水就是 a[i]-(mid-t) (因為在洗衣機裡不能風乾),

這些水沒有別的辦法只能用洗衣機了,所以,我們只需要看這這些a[i]>mid的衣服用的洗衣機時間的總和有沒有超過mid,如果超過了,肯定是不滿足的。

剩下的問題就是如何得到這個t,很簡單,風乾後剩下的水就得用洗衣機來除掉,又知道用了t時間的洗衣機。我們可以推匯出一個式子:k*t=a[i]-(mid-t)  我們需要看的就是這些t的總和於mid的比較關係

移項之後可以得到:t=(a[i]-mid)/(k-1) 

有兩個需要注意的地方:

1、t的結果需要向上取整,因為你的餘數再用一次洗衣機無非就是多1個時間,但是如果你風乾的話,就是用至少1時間,向上取整的方法咱們後面再說

2、這個式子的分母是k-1,當k==1的時候要單獨拿出來討論,不然會RE,也很簡單,其實k==1的時候就相當於風乾唄,直接輸出最大的含水量就可以了

 

看程式碼吧

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n;
long long k;
long long a[100055];
int judge(int x)
{
	long long sum=0;
	long long maxx=0;
	for(int i=0;i<n;i++)
	{
		if(a[i]>x) //自然風乾時間不夠,需要使用洗衣機 
		{
			//sum+=ceil((a[i]-x)/(k-1)); //這裡用ceil函式會WA
			sum+=(a[i]-x+k-2)/(k-1); //分子後面+((分母)-1)這種取整方法我也是第一次見
		}
		if(sum>x) return 0;
		
	}
	return 1;
}
void solve()
{
	int l=1,r=a[n-1];
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(judge(mid)) r=mid-1;
		else l=mid+1;
	}
	cout<<l<<endl;  //這裡是我的習慣問題,while裡邊的判斷條件習慣寫成L<=r
}                   //那麼l=mid+1,r=mid-1,同時最終輸出哪個也得好好想想
                    //當l==r時如果不成立肯定要取L+1,這裡正好不成立後l會加1,所以輸出l
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&a[i]);
	}
	scanf("%lld",&k);
	sort(a,a+n);
	if(k==1)
	{
		printf("%lld\n",a[n-1]);
		return 0;
	}
	solve();
	return 0;
}