题目来源
0青蛙过河 - 蓝桥云课 (lanqiao.cn)
思路
解法一 二分查找+dp法;
二分思路:首先根据n的范围遍历所有可能的值,如果mid值满足题意就一mid为区间右端向左边继续寻找较小值,当区间左右两端相等时输出l值,
check函数的思路:
建立dp[100010],表示第i个位置所能到达的最大值;
状态转移
dp[i]=min(high[i],从j到i的dp[j]的和);
dp[j]=max(dp[j]-dp[i],0);
到达结尾后停止;
判断从dp[n-k]到dp[n-1]的和,如果和大于2*k则返回true,反之返回false;
根据true和false选择合适的二分区间;
解决代码
#include <iostream>
#include <cstring> // For memset
using namespace std;
int n, k;
int a[100010];
int dp[100010];
bool check(int x) {
memset(dp, 0, sizeof(dp));
int temp[100010]; // 临时数组
memcpy(temp, a, sizeof(a)); // 复制a数组的值到temp
for (int i = 1; i <= x; i++)
dp[i] = temp[i];
int j = 1;
for (int i = x + 1; i <= n - 1; i++) {
if (i - j > x)
j++;
if (temp[i] < dp[j]) {
dp[j] -= temp[i];
dp[i] += temp[i];
} else if (temp[i] >= dp[j]) {
temp[i] -= dp[j]; // 使用temp数组代替直接操作a数组
dp[i] += dp[j];
dp[j] = 0;
i--; // Be cautious with this to avoid infinite loops
j++;
}
}
long long ans = 0;
for (int i = n - x; i <= n - 1; i++)
ans += dp[i];
return ans >= 2 * k;
}
int main() {
cin >> n >> k;
if(n==1){
cout<<1;
return 0;
}
for (int i = 1; i <= n - 1; i++)
cin >> a[i];
int l = 1, r = n , mid; // 修正了r的初值为n-1
while (l <r) {
mid = (l + r) >>1;
if (check(mid))
r = mid;
else
l = mid+1 ;
}
cout << l; // 修改为输出l,因为l是满足条件的最小x值
return 0;
}
只有95%的通过率哎!有没有大佬检查一下哪里不合适;
这个是我之前的思路,接下来是我看的别的大佬写的思路,觉得挺好的,就在这里分享给大家
解法二 有效区间法+二分查找
二分思路和之前一样;
有效区间:如果大小为x的距离能够走通,那么前x的距离的和必须要大于2*k,因为要将至少2*k的步数传递给终点,那么每一个区间都至少有一个比2*k大的容器来存储这些步数
解决代码
#include <iostream>
#include <cstring> // For memset
using namespace std;
int n, k;
int a[100010];
int sum[100010];
bool check(int x) {
for(int i=0;i<=n-x-1;i++){
if(sum[i+x]-sum[i]<2*k)
return false;
}
return true;
}
int main() {
cin >> n >> k;
if(n==1){
cout<<1;
return 0;
}
sum[0]=0;
for (int i = 1; i <= n - 1; i++){
cin >> a[i];
sum[i]=a[i]+sum[i-1];
}
int l = 1, r = n , mid; // 修正了r的初值为n-1
while (l <r) {
mid = (l + r) >>1;
if (check(mid))
r = mid;
else
l = mid+1 ;
}
cout << l; // 修改为输出l,因为l是满足条件的最小x值
return 0;
}