二分查找---河中跳房子

每年奶牛们都要举办各种特殊版本的跳房子比赛,包括在河里从一个岩石跳到另一个岩石。这项激动人心的活动在一条长长的笔直河道中进行,在起点和离起点L远 (1 ≤ L ≤ 1,000,000,000) 的终点处均有一个岩石。在起点和终点之间,有 N (0 ≤ N ≤ 50,000) 个岩石,每个岩石与起点的距离分别为 Di (0 < Di < L)。
在比赛过程中,奶牛轮流从起点出发,尝试到达终点,每一步只能从一个岩石跳到另一个岩石。当然,实力不济的奶牛是没有办法完成目标的。
农夫约翰为他的奶牛们感到自豪并且年年都观看了这项比赛。但随着时间的推移,看着其他农夫的胆小奶牛们在相距很近的岩石之间缓慢前行,他感到非常厌烦。他计划移走一些岩石,使得从起点到终点的过程中,最短的跳跃距离最长。他可以移 走除起点和终点外 至多 M (0 ≤ M N ) 个岩石。
请帮助约翰确定移走这些岩石后, 最长可能的最短跳跃距离是多少?

输入
第一行包含三个整数L, N, M,相邻两个整数之间用单个空格隔开。
接下来N行,每行一个整数,表示每个岩石与起点的距离。岩石按与起点距离从近到远给出,且不会有两个岩石出现在同一个位置。
输出
一个整数,最长可能的最短跳跃距离。
样例输入
25 5 2211141721
样例输出
4
解法:
题目分析:
1.序列N中存放的是中间N个石头的位置,起止点的位置没有存储。
2.移除只能移除N中的M个石头,起止点的石头都不能动
3.牛一块石头挨一块的跳,不能连续跳石头。
思路:
1.假设最大的最小跳距离时D,那么和stone[i]距离小于D的时候都要被移除。
2.D的最小取值就是1,最大取值就是把N个石头全移走,直接从起始点调到终止点。
原始思路纠正。
int L,N,M;
cin>>L>>N>>M;
int stone[50010];
for(int i = 1 ;i<= N ;i++)
cin>>stone[i];
max_dis = L;//注意最后一跳,牛是直接跳过去的。
----------------------------------------------------------------
1.没有存起止点的位置
stone[0]=0,stone[N+1]=L
------------------------------------------------------------------
//1.统计两块石头间的最小距离,这个步骤可有可无
int l = 0,r = max_dis;
----------------------------------------------------------------
2.最小的距离至少是1,最大的距离时L
题目说的起止点是stone[0]和stone[N+1],不是stone[1]和stone[N],
后边这一组是可以移除的
l=0, r=L
------------------------------------------------------------------
while(l<=r)
{ D = (r+l)/2;
int start = 0;
total = 0;
----------------------------------------------------------------
3.这时候要从stone[1]一直分析到stone[N+1],把中间该移除
的石头全部移走。
问:题目明确说不能移走stone[N+1]呀?
如果stone[N+1]被移走,则说明它在某个D之内,也就是说这一跳与前边一块石头的距离小于D,而这与题目假设条件--D为最小跳距相矛盾;另一方面则,说明这个D过大,被移走的M过多。因此,系统需要把D往小调,直到令stone[N+1]与紧邻的最后一块石头的距离>=D。
------------------------------------------------------------------
for(int i = 1; i<N i<=N+1 ;i++) //题目明确规定是除起点和终点外的石头,
{
//从i+1开始找,只要和i的距离小于D,就必须移除。
if(stone[i]-stone[start]<D &&i!=N-1 )//终点外的石头,
total++;
else
start = i;
}
if(total>M) //说明D太大了,移除的石头太多了
R = D-1;
else
{
last_D = D;
L = D+1;
}
}

标准答案:
#include<iostream>
#include<math.h>
using namespace std;
int main()
{
int L,N,M;
cin>>L>>N>>M;
long stone[50010];
stone[0]=0;
for(int i = 1;i<=N;i++)
cin>>stone[i];
long max_dis = L;//注意最后一跳,牛是直接跳过去的。
//1.统计两块石头间的最小距离,这个步骤可有可无
long l = 1,r = L;
stone[N+1] = L;
//cout<<"r "<<r<<endl;
long last_D,D;
while(l<=r)
{ D = (r+l)/2;
int start = 0;
int total = 0;
for(int i = 1;i<=N+1;i++) //题目明确规定是除起点和终点外的石头,
{
//从i+1开始找,只要和i的距离小于D,就必须移除。
if(stone[i]-stone[start]<D)
{
total++;
}
else
start = i;
}
if(total>M) //说明D太大了,移除的石头太多了
r = D-1;
else
{
last_D = D;
//cout<<D<<endl;
l = D+1;
}
}
cout<<last_D;
}

### C++ 实现跳房子问题的解决方案 跳房子问题可以通过贪心算法或动态规划来解决,具体取决于问题的复杂度和约束条件。以下是一个基于引用内容实现的完整 C++ 示例代码,结合了用户的需求以及相关引用中的思路。 #### 贪心算法实现 如果问题的目标是最小化步数,则可以使用贪心算法来解决。以下是参考引用[^2]中提到的活动选择问题的类似思路实现的代码: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; struct Step { int start, end; bool operator<(const Step& other) const { return end < other.end; } }; vector<int> jumpHouses(int count, vector<int> steps) { vector<Step> intervals; for (int i = 0; i < steps.size(); ++i) { Step s; s.start = 0; s.end = steps[i]; intervals.push_back(s); } sort(intervals.begin(), intervals.end()); vector<int> result; int lastEnd = 0; for (int i = 0; i < intervals.size(); ++i) { if (intervals[i].start >= lastEnd) { result.push_back(intervals[i].end); lastEnd = intervals[i].end; } } return result; } int main() { int count; cin >> count; vector<int> steps; int n = 0; while (cin >> n) { steps.emplace_back(n); if (cin.get() == '\n') break; } vector<int> res = jumpHouses(count, steps); cout << "["; for (int i = 0; i < res.size(); ++i) { cout << res[i]; if (i != res.size() - 1) cout << ", "; } cout << "]" << endl; return 0; } ``` 此代码实现了基于贪心算法的跳房子问题解决方案。通过排序所有可能的跳跃范围并选择不重叠的跳跃区间,最终输出满足条件的跳跃组合[^2]。 #### 动态规划实现 如果问题需要找到满足特定条件的所有跳跃组合,则可以使用动态规划。以下是参考引用[^4]中提到的单调队列优化方法实现的代码: ```cpp #include <iostream> #include <vector> #include <deque> #include <limits> using namespace std; const int INF = numeric_limits<int>::min(); int check(vector<int>& x, int g) { int n = x.size(); vector<int> f(n, INF); deque<int> q; int now = 0; for (int i = 0; i < n; ++i) { while (now < i && x[i] - x[now] > g) now++; while (!q.empty() && x[q.back()] + g < x[i]) q.pop_back(); if (now <= q.front()) f[i] = f[q.front()] + 1; else f[i] = 1; while (!q.empty() && f[q.front()] <= f[i]) q.pop_front(); q.push_front(i); } return f[n - 1] != INF ? f[n - 1] : -1; } int main() { int n; cin >> n; vector<int> x(n); for (int i = 0; i < n; ++i) cin >> x[i]; int left = 0, right = x[n - 1] - x[0], ans = -1; while (left <= right) { int mid = (left + right) / 2; if (check(x, mid) != -1) { ans = mid; right = mid - 1; } else { left = mid + 1; } } cout << ans << endl; return 0; } ``` 此代码实现了基于动态规划和二分查找跳房子问题解决方案。通过单调队列优化,可以在 O(n log n) 的时间复杂度内解决问题[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值