题目1 : 修补木桶
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
一只木桶能盛多少水,并不取决于桶壁上最高的那块木板,而恰恰取决于桶壁上最短的那块。
已知一个木桶的桶壁由N块木板组成,第i块木板的长度为Ai。
现在小Hi有一个快捷修补工具,每次可以使用修补工具将连续的不超过L块木板提高至任意高度。
已知修补工具一共可以使用M次(M*L<N),如何修补才能使最短的那块木板最高呢?
注意: 木板是环形排列的,第N-1块、第N块和第1块也被视为连续的。
输入
第1行:3个正整数,N, M, L。分别表示木板数量,修补工具使用次数,修补工具每次可以同时修补的木板数。 1≤N≤1,000,1≤L≤20,M*L<N
第2行:N个正整数,依次表示每一块木板的高度Ai,1≤Ai≤100,000,000
输出
第1行:1个整数。表示使用修补工具后,最短木块的所能达到的最高高度
样例说明
第一个修补工具覆盖[2 3 4]
第二个修补工具覆盖[5 8 1]
样例输入
8 2 3 8 1 9 2 3 4 7 5
样例输出
7
假设最终最短的木板长度至少是K,最小需要使用修复工具几次? 为了描述方便我们将这个最少次数记作F(K)。
于是我们的问题变成求出最大的K,满足F(K) <= M。
如果我们将F(K)看成一个函数,随着K增加,我们要修复的木板越来越多,显然F(K)也会越来越大。
换句话说F(K)是单调递增的。我们可以用二分来求出最大的K。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] argv){
Scanner in = new Scanner(System.in);
int N=in.nextInt(),M=in.nextInt(),L=in.nextInt();
int[] height=new int[N];
for(int i=0;i<N;++i)
height[i]=in.nextInt();
int[] sorted=Arrays.copyOf(height,height.length);
Arrays.sort(sorted);
int l=0,r=N-1;
int h=0;
while(l<=r){
int m=(l+r)/2;
h=sorted[m];
if(fix(height,h,L)>M) r=m-1;
else l=m+1;
}
System.out.println(sorted[r]);
}
static int fix(int[] height, int h, int L){
int N=height.length;
int p;
for(p=0;p<N;++p)
if(height[p]<h) break;
if(p==N) return 0;
int min=10000;
for(int i=-L+1;i<=0;++i){
int s=(p+N+i)%N;
int num=0;
for(int j=0;j<N;++j){
int k=(j+s)%N;
if(height[k]<h){
num++;
j+=(L-1);
}
}
min=Math.min(num,min);
}
return min;
}
}