【备战秋招】每日一题:2023.05-B卷-华为OD机试 - 最佳植树距离

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1363

题目描述

按照环保公司要求,小明需要在沙化严重的地区进行植树防沙工作,初步目标是种植一条直线的树带。由于有些区域目前不适合种植树木,所以只能在一些可以种植的点来种植树木。

在树苗有限的情况下,要达到最佳效果,就要尽量散开种植,不同树苗之间的最小间距要尽量大。给你一个适合种情树木的点坐标和一个树苗的数量,请帮小明选择一个最佳的最小种植间距。

例如,适合种植树木的位置分别为 1 , 3 , 5 , 6 , 7 , 10 , 13 1,3,5,6,7,10,13 1,3,5,6,7,10,13树苗数量是 3 3 3,种植位置在 1 , 7 , 13 1,7,13 1,7,13,树苗之间的间距都是 6 6 6,均匀分开,就达到了散开种植的目的,最佳的最小种植间距是 6 6 6

输入描述

1 1 1行表示适合种树的坐标数量。

2 2 2行是适合种树的坐标位置。

3 3 3行是树苗的数量。

例如:

7
1 5 3 6 10 7 13
3

输出描述

最佳的最小种植间距。

备注

  • 位置范围为 1 1 1~ 10000000 10000000 10000000

  • 种植树苗的数量范围 2 2 2~ 10000000 10000000 10000000

  • 用例确保种植的树苗不会超过有效种植坐标数量

样例

输入

7
1 5 3 6 10 7 13
3

输出

6

说明

3 3 3棵树苗分别种植在 1 , 7 , 13 1,7,13 1713位置时,树苗种植的最均匀,最小间距为 6 6 6

思路

问题是要我们求得最大种树间距,我们有直观思路,那就是两边放一棵,然后中间放一棵,不断循环。但是这个思路面临的问题就是很难定义中间的位置。如果使用暴力去查找中间位置,肯定会超时。并且,假设可以种四棵树,两边和中间都种了一棵,那么第四棵树种在哪也是问题。因此,如果按照这种思路解决题目,很难完成。

我们不妨换一个思路。现在给定一个问题:在保证最小种植间距为x的情况下,能否种完所有树?

这个问题如何解决?

我们可以从左到右不断种树,每次遇到间距大于等于x的时候,就种树。这样,就可以解决问题,并且复杂度为O(n),n是树坑的数量。

那么如何就将原问题转为这个问题?

我们可以从小到大枚举种树间距。复杂度为 O ( D i s ) O(Dis) O(Dis), D i s Dis Dis是位置范围。我们发现这样的话总时间复杂度为 O ( n ∗ D i s ) O(n*Dis) O(nDis),会超时。因此,需要优化。我们假设现在种树间距是 x x x,那么如果可以种植成功,那说明 [ 0 , x − 1 ] [0,x-1] [0,x1]种树间距也可以成功,如果失败,那说明 [ x + 1 , D i s ] [x+1,Dis] [x+1,Dis]种树间距也会失败。因此,我们可以采用二分算法。我们定义一个种树间距区间 [ l e f t , r i g h t ] [left,right] [left,right],每次取得区间的中点 m i d mid mid,然后以间距为mid进行种树,查看是否成功,如果成功,那么搜索范围会变为 [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right],不成功,搜索范围为 [ l e f t , m i d − 1 ] [left,mid-1] [left,mid1]。这样可以保证每次区间都减少一半,总的时间复杂度优化为 O ( n ∗ l o g ( D i s ) ) O(n*log(Dis)) O(nlog(Dis))

类似题目推荐

代码

C++

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int n;
vector<int> positions;
int m;

bool check(int minDis) {
    int count = 1;
    int curPos = positions[0];//第一棵树的位置

    for (int i = 1; i < n; i++) {
        if (positions[i] - curPos >= minDis) {//每次种树间距大于等于minDis
            count++;
            curPos = positions[i];
        }
    }

    return count >= m;//如果在间距为minDis的情况下,可以种树成功,那返回true
}

int getResult() {
    sort(positions.begin(), positions.end());//将种树的树坑进行排序

    int low = 1;
    int high = positions.back() - positions[0];
    int ans = 0;

    while (low <= high) {//二分
        int mid = (low + high) / 2;//种树间距的中点

        if (check(mid)) {//如果种树成功
            ans = mid;
            low = mid + 1;
        } else {//如果种树失败
            high = mid - 1;
        }
    }

    return ans;
}

int main() {
    cin >> n;
    positions.resize(n);
    for (int i = 0; i < n; i++) {
        cin >> positions[i];
    }
    cin >> m;

    cout << getResult() << endl;

    return 0;
}

python

# 输入获取
n = int(input())
positions = list(map(int, input().split()))
m = int(input())


def check(minDis):
    count = 1
    curPos = positions[0]#第一棵树的位置

    for i in range(1, n):
        if positions[i] - curPos >= minDis:#每次种树间距大于等于minDis
            count += 1
            curPos = positions[i]

    return count >= m#如果在间距为minDis的情况下,可以种树成功,那返回true


# 算法入口
def getResult():
    positions.sort()#将种树的树坑进行排序

    low = 1
    high = positions[-1] - positions[0]
    ans = 0

    while low <= high:#二分
        mid = (low + high) >> 1#种树间距的中点

        if check(mid):#如果种树成功
            ans = mid
            low = mid + 1
        else:#如果种树失败
            high = mid - 1

    return ans


# 算法调用
print(getResult())

Java

import java.util.Arrays;
import java.util.Scanner;
 
public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
 
    int n = sc.nextInt();
 
    int[] positions = new int[n];
    for (int i = 0; i < n; i++) {
      positions[i] = sc.nextInt();
    }
 
    int m = sc.nextInt();
 
    System.out.println(getResult(n, positions, m));
  }
 
  public static int getResult(int n, int[] positions, int m) {//将种树的树坑进行排序
    Arrays.sort(positions);
 
    int min = 1, max = positions[n - 1] - positions[0];
    int ans = 0;
 
    while (min <= max) {//二分
      int mid = (min + max) >> 1;//种树间距的中点
 
      if (check(positions, m, mid)) {//如果种树成功
        ans = mid;
        min = mid + 1;
      } else {//如果种树失败
        max = mid - 1;
      }
    }
 
    return ans;
  }
 
  public static boolean check(int[] positions, int m, int minDis) {
    int count = 1;
 
    int curPos = positions[0];//第一棵树的位置
    for (int i = 1; i < positions.length; i++) {
      if (positions[i] - curPos >= minDis) {//每次种树间距大于等于minDis
        count++;
        curPos = positions[i];
      }
    }
 
    return count >= m;//如果在间距为minDis的情况下,可以种树成功,那返回true
  }
}

Go

package main

import (
	"fmt"
	"sort"
)

var n int
var positions []int
var m int

func check(minDis int) bool {
	count := 1
	curPos := positions[0]//第一棵树的位置

	for i := 1; i < n; i++ {
		if positions[i]-curPos >= minDis {//每次种树间距大于等于minDis
			count++
			curPos = positions[i]
		}
	}

	return count >= m//如果在间距为minDis的情况下,可以种树成功,那返回true
}

func getResult() int {
	sort.Ints(positions)//将种树的树坑进行排序

	low := 1
	high := positions[n-1] - positions[0]
	ans := 0

	for low <= high {//二分
		mid := (low + high) / 2 //种树间距的中点

		if check(mid) {//如果种树成功
			ans = mid
			low = mid + 1
		} else {//如果种树失败
			high = mid - 1
		}
	}

	return ans
}

func main() {
	fmt.Scan(&n)
	positions = make([]int, n)
	for i := 0; i < n; i++ {
		fmt.Scan(&positions[i])
	}
	fmt.Scan(&m)

	fmt.Println(getResult())
}
 

Js

/* JavaScript Node ACM模式 控制台输入获取 */
const rl = require("readline").createInterface({
  input: process.stdin,
});
 
const lines = [];
rl.on("line", (line) => {
  lines.push(line);
 
  if (lines.length === 3) {
    const n = lines[0] - 0;
    const positions = lines[1].split(" ").map(Number);
    const m = lines[2] - 0;
 
    console.log(getResult(n, positions, m));
 
    lines.length = 0;
  }
});
 
function getResult(n, positions, m) {
  positions.sort((a, b) => a - b);//将种树的树坑进行排序
 
  let min = 1;
  let max = positions[n - 1] - positions[0];
  let ans = 0;
 
  while (min <= max) {//二分
    const mid = (min + max) >> 1;//种树间距的中点
    if (check(positions, m, mid)) {//如果种树成功
      ans = mid;
      min = mid + 1;
    } else {//如果种树失败
      max = mid - 1;
    }
  }
 
  return ans;
}
 
function check(positions, m, minDis) {
  let count = 1;
  let curPos = positions[0];//第一棵树的位置
 
  for (let i = 1; i < positions.length; i++) {
    if (positions[i] - curPos >= minDis) {//每次种树间距大于等于minDis
      count++;
      curPos = positions[i];
    }
  }
 
  return count >= m;//如果在间距为minDis的情况下,可以种树成功,那返回true
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值