枚举、二分问题总结

枚举算法设计步骤:

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;
        }
    }
发布了184 篇原创文章 · 获赞 60 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/StubbornAccepted/article/details/101054072