青蛙过河 蓝桥杯

题意:
小青蛙住在一条河边,它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上。不过,每块石

头有一个高度,每次小青蛙从一块石头起跳,这块石头的高度就会下降1,当石头的高度下降到0 时小青蛙不能再跳到这块石头上(某次跳跃后使石头高度下降到0 是允许的)。

小青蛙一共需要去学校上x天课,所以它需要往返2x 次。当小青蛙具有一个跳跃能力y时,它能跳不超过y的距离。请问小青蛙的跳跃能力至少是多少才能用这些石头上完x次课。

分析:

往返累计2x次 相当于2x个单次 跳跃能力越大 越能保证能够完成任务 所以二分答案mid

有个结论 对于所有长度为mid的区间 如果区间和都 ≥2x 则方案成立 否则不成立

证明:一个长度为mid的区间【L,R】 考虑2x次跳跃现在位置都还小于L 想要越过R 2x个跳跃都会经过区间【L,R】 如果区间和小于2x 则一定不成立

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
template <typename T>
inline T read(T& x) {
  x = 0;
  T w = 1;
  char ch = 0;
  while (ch < '0' || ch > '9') {
    if (ch == '-') w = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    x = x * 10 + (ch - '0');
    ch = getchar();
  }
  return x * w;
}
int h[maxn], sum[maxn];
int n, x;
//判断所有长度为mid的区间之和是否大于等于2x
bool check(int mid)
{
    for(int i = 1; i + mid - 1 <= n; i++)
        if(sum[i + mid - 1] - sum[i - 1] < 2 * x)return false;
    return true;
}
int main()
{
    read(n); read(x);
    for(int i = 1; i <= n - 1; i++)//预处理前缀和
        read(h[i]), sum[i] = sum[i - 1] + h[i];
    sum[n] = 1e9 + 7;
    int left = 1, right = n, ans = n;
    while(left <= right)//二分答案
    {
        int mid = (left + right) >> 1;
        if(check(mid))
            ans = mid, right = mid - 1;//求最小合法解
        else
            left = mid + 1;
    }
    cout<<ans<<endl;
    return 0;
}

当然还有一种朴素的做法

首先最优肯定是每次跳跃尽可能跳最大 这样肯定是从右往左依次选择 所以可以用并查集维护当前点能跳的位置

很好的一种想法

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
         
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;
 
int T,n,m,k;
int va[N];
int vb[N];
int sum[N];
int acc[N];
 
int find(int x)
{
    if(x!=acc[x]) acc[x] = find(acc[x]);
    return acc[x];
}
 
bool check(int mid)
{   
    for(int i=0;i<=n;i++) acc[i] = i,sum[i] = 0;
    for(int i=1;i<n;i++) vb[i] = va[i];
    sum[0] = 2*m;
    for(int i=0;i<n;i++)
    {
        int idx = i+mid;
        if(idx>=n)
        {
            sum[n] += sum[i];
            continue;
        }   
        while(1)
        {
        	idx = find(idx);
            int can = min(vb[idx],sum[i]);
            sum[i] -= can,vb[idx] -= can;
            sum[idx] += can;
            if(sum[i]==0) break;
            acc[find(idx)] = find(idx-1);
            idx = find(idx-1);
            if(idx<=i) return false;
        }
    }
    return true;
}
 
signed main()
{
    IOS;
    cin>>n>>m;
    for(int i=1;i<n;i++) cin>>va[i];
    int l = 1,r = n;
    while(l<r)
    {
        int mid = (l+r)>>1;
        if(check(mid)) r = mid;
        else l = mid+1;
    }
    cout<<l<<"\n";
    return 0;
}

posted @ 2022-10-26 20:55  wzx_believer  阅读(441)  评论(0编辑  收藏  举报