基于二分查找的抽签游戏算法的优化
问题描述:
一个袋子里有n个纸片,纸片上有数字,你随机取出4张纸(有放回),若四张纸的数字和为m,你就赢了,否则你就输了。连续试了几次后你都失败了,于是你想知道到底有没有赢的例子。(n:1到50; m和k[i] 1到10的8次方)
输入输出样例:
输入:
n=3
m=10
k={1,3,5}
输出:yes(1+1+3+5=10)
输入
n=3
m=9
k={1,3,5}
输出:
No
复杂度为N的四次方
复杂度为N的四次方算法分析:
通过四重循环来枚举所有情况。
复杂度为N的四次方算法实现:
package com.renyou.算法;
import java.util.Scanner;
/**
* m=k[a]+k[b]+k[c]+k[d]
* @author 刘仁有
* (10的8次方=>m=>1)
* (50=>n>=1)
* (10的8次方=>k[i]=>1)
*
*/
public class 抽签复杂度N的4次方 {
private final int MAX_N=50;
public static void main(String[] args) {
new 抽签复杂度N的4次方().抽签();
}
public void 抽签(){
int m,n;
int[] k;
Scanner scanner=new Scanner(System.in);
m=scanner.nextInt();
n=scanner.nextInt();
k=new int[n];
for(int i=0;i<n;i++){
k[i]=scanner.nextInt();
}
for(int a=0;a<n;a++){
for(int b=0;b<n;b++){
for(int c=0;c<n;c++){
for(int d=0;d<n;d++){
if(k[a]+k[b]+k[c]+k[d]==m)
System.out.println("找到了这四个值,分别是:"+k[a]+";"+k[b]+";"+k[c]+";"+k[d]);
}
}
}
}
}
}
复杂度为N的三次方LogN
复杂度为N的三次方LogN的算法分析:
检查是否有k[d]使得k[d]=m-k[a]-k[b]-k[c]
复杂度为N的三次方LogN的算法实现:
package com.renyou.算法;
import java.util.Scanner;
/**
* k[d]=m-k[a]-k[b]-k[c]
* @author 刘仁有 (10的8次方=>m=>1) (1000=>n>=1) (10的8次方=>k[i]=>1)
*
*/
public class 抽签复杂度N的三次方LogN {
private final int MAX_N = 1000;
private int[] k ;
public static void main(String[] args) {
new 抽签复杂度N的三次方LogN().抽签();
}
public void 抽签() {
int m, n;
/******** 控制台输入数据 **********/
Scanner scanner = new Scanner(System.in);
m = scanner.nextInt();
n = scanner.nextInt();
k=new int[n];
for (int i = 0; i < n; i++) {
k[i] = scanner.nextInt();
}
/********** 数组快速排序 **************/
sort(k, 0, k.length - 1);
/*********** N的三次方的for循环 ***************/
for (int a = 0; a < n; a++) {
for (int b = 0; b < n; b++) {
for (int c = 0; c < n; c++) {
if(bineary_search(m-k[a]-k[b]-k[c])){
System.out.println("找到了这四个值,分别是:"+k[a]+";"+k[b]+";"+k[c]+";"+(m-k[a]-k[b]-k[c]));
}
}
}
}
}
/**
*
* @param 要查找的值
* @return ture or false
*/
public boolean bineary_search(int value) {
int low = 0;
int high = k.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
int midValue = k[mid];
if (value < midValue) {
high = mid - 1;
} else if (value > midValue) {
low = mid + 1;
} else {
return true;
}
}
return false;
}
/**
* 快速排序
*
* @param array
* 待快速排序数组
* @param first
* 待快速排序序列的最左边所在位置
* @param end
* 待快速排序序列的最右边所在位置
*/
public void sort(int[] array, int first, int end) {
int pivot = 0;// 轴值记录所在位置
if (first < end) {// 区间长度大于一才会执行一次划分,否则递归结束
pivot = partition(array, first, end);// 进行一次划分
sort(array, first, pivot - 1);// 递归地对左序列进行快速排序
sort(array, pivot + 1, end);// 递归地对右序列进行快速排序
}
}
/**
* 快速排序一次划分算法
*
* @param array
* 待快速排序数组
* @param first
* 待快速排序序列的最左边所在位置
* @param end
* 待快速排序序列的最右边所在位置
* @return 每次划分序列轴值最后记录所在位置
*/
public int partition(int[] array, int first, int end) {
while (first < end) {
while (first < end && array[first] <= array[end]) {// 右侧扫描
end--;
}
if (first < end) {
swap(array, first, end);
first++;
}
while (first < end && array[first] <= array[end]) {// 左侧扫描
first++;
}
if (first < end) {
swap(array, end, first);
end--;
}
}
return first;
}
/**
*
* @param array
* 待交换数组
* @param i
* 待交换数组的一个索引
* @param j
* 待交换数组的另一个索引
*/
public void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
复杂度为N的二次方LogN
复杂度为N的二次方LogN的算法分析:
检查是否有k[c]+k[d]使得k[c]+k[d]=m-k[a]-k[b]
复杂度为N的二次方LogN的算法实现:
package com.renyou.算法;
import java.util.Scanner;
/**
* k[c]+k[d]=m-k[a]-k[b]
*
* @author 刘仁有 (10的8次方=>m=>1) (1000=>n>=1) (10的8次方=>k[i]=>1)
*
*/
public class 抽签复杂度N的二次方LogN {
private final int MAX_N = 1000;
private int[] k;
private int[] kcd;// 存储 c和d的和的数组
public static void main(String[] args) {
new 抽签复杂度N的二次方LogN().抽签();
}
public void 抽签() {
int m, n;
/******** 控制台输入数据 **********/
Scanner scanner = new Scanner(System.in);
m = scanner.nextInt();
n = scanner.nextInt();
k = new int[n];
for (int i = 0; i < n; i++) {
k[i] = scanner.nextInt();
}
/*********** 存储 c和d的和 **********/
kcd = new int[n * n];
for (int c = 0; c < n; c++) {
for (int d = 0; d < n; d++) {
kcd[c * n + d] = k[c] + k[d];
}
}
/********** kcd数组快速排序 **************/
sort(kcd, 0, kcd.length - 1);
/*********** N的三次方的for循环 ***************/
for (int a = 0; a < n; a++) {
for (int b = 0; b < n; b++) {
if (bineary_search(m - k[a] - k[b])) {
System.out.println("找到了这四个值");
}
}
}
}
/**
*
* @param 要查找的值
* @return ture or false
*/
public boolean bineary_search(int value) {
int low = 0;
int high = kcd.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
int midValue = kcd[mid];
if (value < midValue) {
high = mid - 1;
} else if (value > midValue) {
low = mid + 1;
} else {
return true;
}
}
return false;
}
/**
* 快速排序
*
* @param array
* 待快速排序数组
* @param first
* 待快速排序序列的最左边所在位置
* @param end
* 待快速排序序列的最右边所在位置
*/
public void sort(int[] array, int first, int end) {
int pivot = 0;// 轴值记录所在位置
if (first < end) {// 区间长度大于一才会执行一次划分,否则递归结束
pivot = partition(array, first, end);// 进行一次划分
sort(array, first, pivot - 1);// 递归地对左序列进行快速排序
sort(array, pivot + 1, end);// 递归地对右序列进行快速排序
}
}
/**
* 快速排序一次划分算法
*
* @param array
* 待快速排序数组
* @param first
* 待快速排序序列的最左边所在位置
* @param end
* 待快速排序序列的最右边所在位置
* @return 每次划分序列轴值最后记录所在位置
*/
public int partition(int[] array, int first, int end) {
while (first < end) {
while (first < end && array[first] <= array[end]) {// 右侧扫描
end--;
}
if (first < end) {
swap(array, first, end);
first++;
}
while (first < end && array[first] <= array[end]) {// 左侧扫描
first++;
}
if (first < end) {
swap(array, end, first);
end--;
}
}
return first;
}
/**
*
* @param array
* 待交换数组
* @param i
* 待交换数组的一个索引
* @param j
* 待交换数组的另一个索引
*/
public void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}