枚舉算法設計步驟:
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;
}
}