枚舉、二分問題總結

枚舉算法設計步驟:

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;
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章