枚举算法设计步骤:
1.确定枚举对象
2.逐一列举可能解
3.逐一验证可能解
例题:数组配对—枚举
题目描述
给你一个长度为n的数组和一个正整数k,问从数组中任选两个数使其和是k的倍数,有多少种选法
对于数组a1=1 , a2=2 , a3=2而言:
(a1,a2)和(a2,a1)被认为是同一种选法;
(a1,a2)和(a1,a3)被认为是不同的选法。
输入数据
第一行有两个正整数n,k。n<=1000000,k<=1000000 第二行有n个正整数,每个数的大小不超过1e9
输出数据
选出一对数使其和是k的倍数的选法个数
样例输入
5 6
1 2 3 4 5
样例输出
2
样例说明
a1+a5=6,a2+a4=6,都是6的倍数
所以符合条件的选法有(1,5),(2,4)
思路分析:
枚举对象如果是长度为n的数组中的每个元素,那么需要两层循环来判断,时间复杂度O(N^2).尝试降低降低时间复杂度。根据数学推导:
(a[i]+a[j])%k=0 -----> (a[i]%k+a[j]%k)%k=0
可以将枚举对象转化为0~k.
代码:
package homeWork.SeatWork1;
import java.util.Scanner;
/**
* Created by fangjiejie on 2019/9/18.
*/
public class MultipleOfK {
/*
*给你一个长度为n的数组和一个正整数k,问从数组中任选两个数使其和是k的倍数,有多少种选法
* (a[i]+a[j])%k=0 -----> (a[i]%k+a[j]%k)%k=0
* */
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int k = scanner.nextInt();
int a[] = new int[k];
for (int i = 0; i < n; i++) {
int tmp = scanner.nextInt();
a[tmp % k]++;
}
int count = (a[0] * (a[0] - 1)) / 2;
for (int i = 1; i <= k / 2; i++) {
if (a[i] != 0 && a[k - i] != 0) {
if (i == k - i) {
count += (a[i] * (a[i] - 1)) / 2;
} else {
count += a[i] * a[k - i];
}
}
}
System.out.print(count);
}
}
二分问题一般化模型:
例题:绳子切割—二分+枚举
题目描述
我们有n条的绳子。现在从这些绳子中切割出来m条长度相同的绳子,问这m条绳子最长有多长?
输入数据
第一行输入两个数字,n(1<=n<=1000)为绳子数目,m(1<=m<=1000)为需要切割出的相同长度的绳子数目 随后n个正整数,表示原始绳子的长度(<=10000)
输出数据
每组输出一行结果,表示切割后绳子的最长长度(保留两位小数)
样例输入
4 5
5 6 7 8
样例输出
4.00
思路分析
需要找到切割后绳子的最长长度,将枚举对象设为绳子长度,枚举范围就是0~原始绳子中最长的长度。对每一个长度值判断是否可以切割成功。然后利用二分的思想不断逼近最大值。
代码
package homeWork.SeatWork1;
import java.util.Scanner;
/**
* Created by fangjiejie on 2019/9/18.
*/
public class LongestOfCut {
/*
* 我们有n根的木棍。现在从这些木棍中切割出来m条长度相同的木棍,问这m根木棍最长有多长?
* */
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
double a[] = new double[n];
double maxS = -1;
for (int i = 0; i < n; i++) {
a[i] = scanner.nextInt();
if (maxS < a[i]) {
maxS = a[i];
}
}
double left = 0;
double right = maxS;
while (left < right) {
double mid = (left + right) / 2.0;
if (isCouldCut(a, mid, m)) {
left = mid + 0.001;
} else {
right = mid - 0.001;
}
}
System.out.printf("%.2f", left);
}
private static boolean isCouldCut(double[] a, double mid, int m) {
int num = 0;
for (int i = 0; i < a.length; i++) {
num += a[i] / mid;
}
if (num >= m) return true;
return false;
}
}
例题:移除石头—二分+枚举
题目描述
有一条河,河中间有一些石头,已知石头的数量和相邻两块石头之间的距离。现在可以移除一些石头,问最多移除m块石头后(首尾两块石头不可以移除),相邻两块石头之间的距离的最小值最大是多少。
输入数据
第一行输入两个数字,n(2<=n<=1000)为石头的个数,m(0<=m<=n-2)为可移除的石头数目 随后n-1个数字,表示顺序和相邻两块石头的距离d(d<=1000)
输出数据
输出最小距离的最大值
样例输入
4 1
1 2 3
样例输出
3
思路分析
最小距离的最大值是指:在每一次移除方案中,取任意相邻两块石头距离中最小的值。比对每一次移除方案,找到最大的那个。
二分枚举距离d。判断 在 以d作为相邻两块石头距离的最小值 的条件下 最多移除m块石头,是否可以实现。不断逼近最大的d。注意d枚举范围是0~
代码
package homeWork.SeatWork1;
import java.util.Scanner;
/**
* Created by fangjiejie on 2019/9/18.
*/
public class MaxOfMinDistance {
/*
* 有一条河,河中间有一些石头,已知石头的数量和相邻两块石头之间的距离。现在可以移除一些石头,问最多移除m块石头后(首尾两块石头不可以移除),相邻两块石头之间的距离的最小值最大是多少。
* 最小距离最大值的含义:在每个移法中,找到相邻石头距离的最小值。 在所有移法中找到最大的那个值
* */
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int m=scanner.nextInt();
int a[]=new int[n];//代表第i块石头和第0块石头的距离
a[0]=0;
for(int i=1;i<=n-1;i++){
int tmp=scanner.nextInt();
a[i]=tmp+a[i-1];
}
int max=-1;
int left=0,right=1000*1000+5;///?
while (left<=right){
int mid=(left+right)/2;//4
if(isCouldRemove(a,mid,m)){
max=mid;
left=mid+1;//4
}else{
right=mid-1;//5
}
}
System.out.print(max);
}
//判断移除小于等于m块石头后,是否可以实现石头间最小距离为distance
private static boolean isCouldRemove(int[] a, int distance, int m) {
int last=a[0],cnt=0;
for(int i=1;i<a.length;i++){
if(a[i]-last<distance){
cnt++;
}else{
last=a[i];
}
}
if(cnt>m){
return false;
}
return true;
}
}
例题:浮点数的整数次方
Implement pow(x, n), which calculates x raised to the power n (xn).
Example 1:
Input: 2.00000, 10
Output: 1024.00000
Example 2:
Input: 2.10000, 3
Output: 9.26100
Example 3:
Input: 2.00000, -2
Output: 0.25000
Explanation: 2-2 = 1/22 = 1/4 = 0.25
Note:
-100.0 < x < 100.0
n is a 32-bit signed integer, within the range [−231, 231 − 1]
/*
* 浮点数的整数次方
* 二分解决
* 注意:n为负数时,结果为n的绝对值的倒数,还要注意控制n超界的情况
* */
public double myPow(double x, int n) {
if(n >= Integer.MAX_VALUE || n <= Integer.MIN_VALUE){
if(x == 1){
return 1;
}
if(x == -1){
return n % 2 == 0 ? 1 : -1;
}
return 0;
}
double result = floatPow(x, Math.abs(n));
if (n > 0) {
return result;
} else {
return 1 / result;
}
}
public double floatPow(double x, int n) {
if (n == 1) return x;
if (n == 0) return 1;
double k = myPow(x, n / 2);
if (n % 2 == 0) {
return k * k;
} else {
return k * k * x;
}
}