剪绳子(AcWing 680)

剪绳子

https://www.acwing.com/problem/content/submission/code_detail/3421444/

描述

有N根绳子,第i根绳子长度为LiLi,现在需要M根等长的绳子,你可以对N根绳子进行任意裁剪(不能拼接),请你帮忙计算出这M根绳子最长的长度是多少。

输入格式

第一行包含2个正整数N、M,表示原始绳子的数量和需求绳子的数量。

第二行包含N个整数,其中第 i 个整数LiLi表示第 i 根绳子的长度。

输出格式

输出一个数字,表示裁剪后最长的长度,保留两位小数。

数据范围

1≤N,M≤1000001≤N,M≤100000,
0<Li<1090<Li<109

输入样例:

3 4
3 5 4

输出样例:

2.50

样例解释

第一根和第三根分别裁剪出一根2.50长度的绳子,第二根剪成2根2.50长度的绳子,刚好4根。

解答

大致思路

  • 每次截取长度为d的绳子, n根绳子总共可以截取k条该绳段
  • 如果 k > m, 可能恰好,也可能截取的长度过短
  • 如果 k < m, 说明截取的长度过长, 需要减小d
  • 解题的关键就是: 如何判断当 k > m时,是恰好还是截取的长度过短?

思路一(从小累加或者从大累减):

  • 从d=0.01(最小单位开始)
  • 如果k > m, 就延长一点截取长度 d = d+ 0.01
  • 如果 k < m, 说明截取的长度太长了,那么 d - 0.01就是最佳结果
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
       Scanner scanner = new Scanner(System.in);
       int n = scanner.nextInt();
       int m = scanner.nextInt();

       int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            int x = scanner.nextInt();
            arr[i] = x;
        }
        Arrays.sort(arr);

        double d = 0;
        int k = Integer.MAX_VALUE;  //可以分为k条长度为d的绳子
        while (k >=  m) {
            d += 0.01;
            k = 0;
            for (int i = 0; i < n; i++) {
                k += (int)arr[i]/d;
            }
        }
        System.out.println(String.format("%.2f", d-0.01));
    }


}

思路二(从两边向中间趋近,即二分法)

  • 从l=0.01, r = 最长绳子的长度
  • 截取长度取中间值mid = (l + r)/2;
  • 如果k < m, 说明太长 r = mid
  • 如果k > m, l = mid
  • 直到 l==r,说明截取的长度刚刚好。(差值小于0.01就算相等)

为了能够进一步减少迭代的次数

  • l = 最长一个绳子 / m, r = sum/m
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();

        long[] arr = new long[n];
        long sum = 0;
        for (int i = 0; i < n; i++) {
            long x = scanner.nextLong();
            arr[i] = x;
            sum += x;
        }
        Arrays.sort(arr);

        double l = arr[0]*1.00 / m;
        double r = sum*1.00 / m;
        if(l > r){
            double tmp = l;
            l = r;
            r = tmp;
        }

        while( r - l >= 0.01){
            double mid = (r + l)/2;
            int count = 0;
            for (int i = 0; i < n; i++) {
                count += (long) arr[i]/mid;
            }
            if(count < m){
                r = mid;
            }else{
                l = mid;
            }
        }

        System.out.println(String.format("%.2f", (l+r)/2 ) );
    }


}

posted @ 2021-01-17 20:27  loser_wang  阅读(165)  评论(0编辑  收藏  举报