本题简单解析
考察所谓的“二分查找”,但我使用JAVA实现二分查找的方法,AC时竟然比先前自己写的代码通过的测试点还少。
以下是代码运行时间的比较:
测试用例(PAT标准用例1):
16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13
版本 | 运行时间 | 测试用例未通过的序号 |
---|---|---|
自己写的版本1 | 5295904300ns | 2,4,5 |
自己写的版本2 | 1823066099ns | 2,4,5 |
根据柳神博客写的二分查找版本 | 4099952500ns | 1,2,3,4,5 |
自己的版本1与版本2区别在于进入循环时判断某一值是否已经大于最小值,若是则跳过该循环。
自己写的版本的思路
很简单,每读入一个数字,把该数字分别与先前的数组内元素加和,并将该数字存到对应位置。
以PAT标准用例1为例
16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13
第一次读取后数组:
3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
第二次读取后数组:
5 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
第三次读取后数组:
6 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0
......
按照如上思路该数组第一个元素代表从第一个钻石开始叠加的价值,第二个元素代表从第二个钻石开始叠加的价值…
并设置mincost变量代表最小的钻石价值支出。
二分查找的思路
:将每个钻石的价值按照顺序逐次叠加,从题目给定第一个钻石开始,设每次循环取到的钻石为Z,且注意(第一次循环设该循环取到的钻石价值为0,第二次循环取到题目给定钻石串的第一个)每次循环找到一个符合“Z作为
本次循环结果钻石串的第一个且
该结果钻石串总价值大于等于且最接近题目给定所要支付的价值”
的钻石串,然后根据该循环结果与最小价值、题目给定所要求支付的价值进行比较,从而得到结果。
版本1、2代码
版本2就是把下面代码里面的三行注释去掉注释符:
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scaner = new Scanner(System.in);
int diamondsNum = scaner.nextInt();
int totalMoney = scaner.nextInt();
int[] diamondsString = new int[diamondsNum];
boolean flag = false;
ArrayList<Integer> start = new ArrayList<>();
ArrayList<Integer> end = new ArrayList<>();
int mincost = Integer.MAX_VALUE;
int[] addValue = new int[diamondsNum];
for(int i = 0 ; i < diamondsNum ; i++) {
diamondsString[i] = scaner.nextInt();
addValue[i] = 0;
for(int j = i ; j >= 0 ; j--) {
addValue[j] += diamondsString[i];
// if(addValue[j] > mincost) {
// break;
// }
if(addValue[j] == totalMoney) {
flag = true;
if((i + 1) > (j + 1))
System.out.println((j + 1) + "-" + (i + 1));
else
System.out.println((i + 1) + "-" + (j + 1));
}
else if(addValue[j] > totalMoney) {
if(addValue[j] < mincost) {
mincost = addValue[j];
start.clear();
end.clear();
start.add((i + 1));
end.add((j + 1));
}
else if(addValue[j] == mincost) {
start.add((i + 1));
end.add((j + 1));
}
}
}
}
scaner.close();
if(!flag) {
for(int i = 0 ; i < start.size() ; i++) {
if(start.get(i) < end.get(i))
System.out.println(start.get(i) + "-" + end.get(i));
else
System.out.println(end.get(i) + "-" + start.get(i));
}
}
}
}
二分查找版本
根据柳神博客改写:
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
static int diamondsNum = -1;
static int totalMoney = -1;
static int[] diamondsString;
static int[] binaryCheck(int start) {
int[] result = new int[2];
int left = start - 1;
int leftValue;
if(left == -1)
leftValue = 0;
else
leftValue = diamondsString[left];
int right = diamondsNum - 1;
while(left < right) {
int mid = (left + right)/2;
if(diamondsString[mid] - leftValue >= totalMoney)
right = mid;
else
left = mid + 1;
}
result[0] = right;
result[1] = diamondsString[right] - leftValue;
return result;
}
public static void main(String[] args) {
Scanner scaner = new Scanner(System.in);
diamondsNum = scaner.nextInt();
totalMoney = scaner.nextInt();
diamondsString = new int[diamondsNum];
ArrayList<Integer> start = new ArrayList<>();
ArrayList<Integer> end = new ArrayList<>();
int mincost = Integer.MAX_VALUE;
for(int i = 0 ; i < diamondsNum ; i++) {
diamondsString[i] = scaner.nextInt();
if(i >= 1)
diamondsString[i] += diamondsString[i - 1];
}
scaner.close();
for(int i = 0 ; i < diamondsString.length ; i++) {
int[] temp = binaryCheck(i);
int left = i;
int right = temp[0];
int tempSum = temp[1];
if(tempSum >= totalMoney) {
if(tempSum < mincost) {
start.clear();
end.clear();
mincost = tempSum;
start.add(left + 1);
end.add(right + 1);
}
else if(tempSum == mincost) {
start.add(left + 1);
end.add(right + 1);
}
}
}
for(int i = 0 ; i < start.size() ; i++) {
System.out.println(start.get(i) + "-" + end.get(i));
}
}
}
分析
感觉可能是java天生运行速度就比不上c++,而且题目给的测试用例规模也挺大的。另外我该写柳神的代码可能也存在错误,导致改写的JAVA代码运行时间长,恳请看到这篇博客的各位大佬斧正,小弟不胜感激。