这是第一次做答案探索类型的题目
package com.company.real;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
//按照何pro的思路,二分进行答案搜索
//因为答案是线性的,a满足的话,a-1肯定满足
//二分进行探索,找到a满足,a+1不满足的数字,答案就是a
public class JianDao2fen {
static int N;//格子的数量
static int K;//剪的次数,0 ~ N-1
static int[] arrN;
static int[] tree_max;//最大值
static int[] tree_min;//最小值
static int findex=0;
public static void main(String[] args) throws Exception{
//System.setIn(new FileInputStream("C:\\\\Users\\\\XAGDC\\\\Desktop\\\\sw\\\\PRO\\\\2Pro 剪子 Scissors\\sample_input.txt"));
System.setIn(new FileInputStream("C:\\Users\\XAGDC\\Desktop\\sw\\PRO\\2Pro 剪子 Scissors\\eval_input.txt"));
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st=new StringTokenizer(reader.readLine());
int T=Integer.parseInt(st.nextToken());
for (int zz = 0; zz <T ; zz++) {
long startTime=System.currentTimeMillis();
st=new StringTokenizer(reader.readLine());
N=Integer.parseInt(st.nextToken());//格子的数量
K=Integer.parseInt(st.nextToken());//剪的次数,0 ~ N-1*/
arrN=new int[N];
st=new StringTokenizer(reader.readLine());
for (int i = 0; i <N ; i++) {
arrN[i]=Integer.parseInt(st.nextToken());
}
long endTime=System.currentTimeMillis();
if(N==1){
System.out.printf("#%d %d \n",(zz+1),0);
continue;
}
if(N==2 && K==1){
System.out.printf("#%d %d \n",(zz+1),0);//长度是2,切了一次,分数是0
continue;
}
initTree();
int max=pro(arrN,K);
endTime=System.currentTimeMillis();
System.out.printf("#%d %d \n",(zz+1),max);
}
}
public static int pro(int[] arr,int c){
int min=Integer.MAX_VALUE;
int max=Integer.MIN_VALUE;
for (int i = 0; i <arr.length ; i++) {
if(arr[i]<min){
min=arr[i];
}
if(arr[i]>max){
max=arr[i];
}
}
int high=max;
int low=0;
while(low<=high){
int mid=(low+high)/2;
boolean re=checkCon(arr,c,mid);
// System.out.printf("check c:%d v:%d result=> %b\n",c,mid,re);
if(re){//当前数字正确
boolean re2=checkCon(arr,c,mid+1);
if(!re2){//当前数字+1不正确
return mid+1;
}
low=mid+1;
}else{//当前数字不正确
/*boolean re2=checkCon(arr,c,mid-1);//当前数字-1正确
if(re2){
return mid-1;
}*/
high=mid-1;
}
}
//System.out.printf("process c:%d =%d \n",c,low);
return low;
}
public static boolean checkCon(int[] arr,int count,int v){//按照价值v,切c次 可以切,返回true
//按照价值v,切c次,可以切,就增加v
//v增加,会导致count减少
int start=1;
int sv=0;
for (int i = 1; i <=N ; i++) {
sv=soRan(findex+start,findex+i);
//System.out.printf("start:%d end:%d =>sv:%d \n",(findex+start+1),(findex+end+1),sv);
if(sv>v){
//System.out.printf("start:%d end:%d \n",(findex+start+1),(findex+i+1));
if(count<=0){//说明还能继续切,当前数字还可以增加
return true;
}
count--;
start=i;
}
}
return false;
}
static int query_max (int start,int end) {
int max = Integer.MIN_VALUE;
int s = start;
int e = end;
while(s<=e){
if(s%2==1) {
max=Integer.max(tree_max[s],max);
}
if(e%2==0) {
max=Integer.max(tree_max[e],max);
}
s=(s+1)>>1;
e=(e-1)>>1;
}
return max;
}
static void update_max(int index,int value) {
while (index>0) {
if(tree_max[index]<value) {
tree_max[index] = value;
}
index=index>>1;
}
}
public static void initTree(){
int l=1;
while (l<N){
l=l*2;
}
tree_max = new int[2*l];//计算出数组的大小
tree_min = new int[2*l];
Arrays.fill(tree_min,Integer.MAX_VALUE);
int fromIndex = l;//这个是实际数据的开始, fromIndex+1 方的是数组里的第一个数字
findex=fromIndex;
for (int i = 0; i < arrN.length; i++) {
int index=fromIndex+i+1;
int v = arrN[i];
update_max(index,v);
update_min(index,v);
}
}
static int query_min (int start,int end) {
int min = Integer.MAX_VALUE;
int s = start;
int e = end;
while(s<=e){
if(s%2==1) {
min=Integer.min(tree_min[s],min);
}
if(e%2==0) {
min=Integer.min(tree_min[e],min);
}
s=(s+1)>>1;
e=(e-1)>>1;
}
return min;
}
static void update_min(int index,int value) {
while (index>0) {
if(tree_min[index]>value) {
tree_min[index] = value;
}
index=index>>1;
}
}
public static int soRan(int start,int end){//计算一个数组区间的费用,前面包含,后面包含
int min=query_min(start,end);
int max=query_max(start,end);
return max-min;
}
}