POJ3104 Drying —— 二分

时间:2021-06-09 21:55:23

题目链接:http://poj.org/problem?id=3104

Drying
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 18378   Accepted: 4637

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

Source

Northeastern Europe 2005, Northern Subregion
 
 
 
 
题解:
1.二分时间。
2.枚举每一件衣服,然后判断在限制的时间内是否能将所有时间烘干。
 
 
问:怎么判断?
 
思维一:模拟过程(即以时间为线程),每次拿一件进行“试烘烤”,如果这件衣服在剩余的时间内,能够自己风干,则把它扔在一边;否则,则需要对其进行烘烤,使得其经过烘烤后,在剩余的时间内能够自己风干,然后再把它扔一边。注意:在烘烤一件衣服的时候,其他未干的衣服也会蒸发水汽,所以需要记录当前时间,当拿到一件衣服时,就知道它还剩多少水分。
 
思维二:不模拟过程了,按照逻辑来做。假设当前二分到的时间为limit。 枚举每一件衣服,对于当前衣服:如果它可以在limit时间之内风干,扔在一边;否则,对其进行烘烤,使得其经过烘烤后,能够在“除烘烤时间的剩余时间”内自己风干,既可以扔在一边。对每一件衣服的烘烤时间求和,如果和小于等于limit,则limit合法,否则不合法。实际上,把思维一的代码稍微做一下变形,就可以得到思维二的代码。
 
 
过程中出现的一些错误:
1.在写思维一的test()函数时, 直接修改了原始数组,导致下一次调用test()的时候,数据已不是原来的数据了,应该开多个tmp[]数组,作为原始数组的副本,然后直接对tmp[]进行修改。(这个错误找了好久,而且前不久也犯了一次,心塞, 以此为鉴!!)
2.由于答案最大仅为1e9,在int范围内,所以就认为过程中不会出现溢出。但在test()函数内,time的值却可能会超出int, 因为a[i]<=1e9, n<=1e5,假如输入的k是2,且又不在time出现超出limit的时候直接返回, 而让其一直累加到最后,那就会溢出了。所以,在test()函数内,如果一旦出现“不可能”或“肯定能”就直接返回,而不要放任其继续操作,直到最后才返回,以造成预想不到的后果。
 
 
 
思维一错误代码:
 #include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
#define ms(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
const double EPS = 1e-;
const int INF = 2e9;
const LL LNF = 2e18;
const int MAXN = 1e5+; int n, k, a[MAXN]; bool test(int mid)
{
int time = , left = mid;
for(int i = n; i>=; i--)
{
a[i] -= time; // a[i]的值已经发生改变了!!!!!! 应该另开一个数组用于操作,以保证原始数值不被修改
if(a[i]<=left) continue;
int t = (a[i]-left)/(k-);
if((a[i]-left)%(k-)) t++;
time += t;
left -= t;
if(left<) return false;
}
return true;
} int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i = ; i<=n; i++)
scanf("%d", &a[i]);
scanf("%d", &k);
sort(a+, a++n);
if(k==)
{
printf("%d\n", a[n]);
continue;
} int l = , r = a[n];
while(l<=r)
{
int mid = (l+r)>>;
if(test(mid))
r = mid - ;
else
l = mid + ;
}
printf("%d\n", l);
}
}
思维二正确代码:
 #include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
#define ms(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
const double EPS = 1e-;
const int INF = 2e9;
const LL LNF = 2e18;
const int MAXN = 1e5+; int n, k, a[MAXN];
int tmp[MAXN]; bool test(int mid) //模拟过程
{
//time为当前时间, left为剩余时间, time与left之和恒为mid
int time = , left = mid;
memcpy(tmp, a, sizeof(tmp)); //!!!!!!
for(int i = n; i>=; i--)
{
tmp[i] -= time; //过了多长时间, 就风干了多少水。
if(tmp[i]<=left) continue; //如果剩下的时间可以让这件衣服完全风干, 则跳过;否则下一步
int t = (tmp[i]-left)/(k-); //t为使得这件衣服可以风干的最少烘干次数
if((tmp[i]-left)%(k-)) t++; //向上取整
time += t; //更新当前时间和剩余时间
left -= t;
if(left<) return false; //如果超额,则不能满足条件
}
return true;
} int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i = ; i<=n; i++)
scanf("%d", &a[i]);
scanf("%d", &k);
sort(a+, a++n);
if(k==) //当k为1时, 烘干机没用了, 全部自己风干。
{
printf("%d\n", a[n]);
continue;
} int l = , r = a[n];
while(l<=r)
{
int mid = (l+r)>>;
if(test(mid))
r = mid - ;
else
l = mid + ;
}
printf("%d\n", l);
}
}
思维二:
 #include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
#define ms(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
const double EPS = 1e-;
const int INF = 2e9;
const LL LNF = 2e18;
const int MAXN = 1e5+; int n, k, a[MAXN]; bool test(LL limit)
{
int sum = ;
for(int i = n; i>=; i--)
{
if(a[i]<=limit) continue; //直接风干
int t = (a[i]-limit)/(k-); //t为使得这件衣服可以风干的最少烘干次数
if((a[i]-limit)%(k-)) t++; //向上取整
sum += t; //时间累加
if(sum>limit) return false; //超过时间限制, 退出
}
return true; //满足条件
} int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i = ; i<=n; i++)
scanf("%d", &a[i]);
scanf("%d", &k);
sort(a+, a++n);
if(k==) //当k为1时, 烘干机没用了, 全部自己风干。
{
printf("%d\n", a[n]);
continue;
} int l = , r = a[n];
while(l<=r)
{
int mid = (l+r)>>;
if(test(mid))
r = mid - ;
else
l = mid + ;
}
printf("%d\n", l);
}
}