题目描述
按照环保公司要求,小明需要在沙化严重的地区进行植树防沙工作,初步目标是种植一条直线的树带。由于有些区域目前不适合种植树木,所以只能在一些可以种植的点来种植树木。
在树苗有限的情况下,要达到最佳效果,就要尽量散开种植,不同树苗之间的最小间距要尽量大。给你一个适合种情树木的点坐标和一个树苗的数量,请帮小明选择一个最佳的最小种植间距。
例如,适合种植树木的位置分别为 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 1,7,13位置时,树苗种植的最均匀,最小间距为 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(n∗Dis),会超时。因此,需要优化。我们假设现在种树间距是 x x x,那么如果可以种植成功,那说明 [ 0 , x − 1 ] [0,x-1] [0,x−1]种树间距也可以成功,如果失败,那说明 [ 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,mid−1]。这样可以保证每次区间都减少一半,总的时间复杂度优化为 O ( n ∗ l o g ( D i s ) ) O(n*log(Dis)) O(n∗log(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
}