题意:
就是小青蛙在0点,学校在n点,然后1到n-1个地方有石头,而且有个高度,小青蛙每跳一次这个石头会降低一格,如果<=0不能再去跳了。问你小青蛙跳跃距离最小可以多少满足他x次上学,x次上学分别是去x次,来x次,一共2*x次过河。
思考:
刚看到这题,这去x次来x次,这怎么贪心呢,如果每次尽量往远的跳肯定不行,这样来的时候可能就没机会了。实际上你转化一下,去x来x,为何不能转化成去2x次呢?这样就能每次贪心跳最远的点了。为什么能这样转化,因为你怎么去,我就怎么回来,所以和去x次一摸一样。现在就好了,就是对于刚开始在0点往后跳,尽量往远的跳如果这个点的高度不够,那么就往前面的石头走,注意不能小于当前小青蛙在的位置。发现这样做的话是nn的复杂度,其实发现对于一个点如果高度为0了,那么就不需要了,所以并查集记录一下路径就可以了。
当然还有一种感性的做法,对于二分当前的跳跃距离mid,如果任何一个长度为mid的区间和都是>=2x的那么就是满足的,你想象一下,每个长度为mid的都够2x次去转移,那么肯定是合法的。思维多多开拓一下吧。
代码:
并查集记录路径
#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;
}
感性做法:
#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 sum[N];
bool check(int mid)
{
for(int i=1;i+mid-1<n;i++)
{
int l = i,r = i+mid-1;
if(sum[r]-sum[l-1]<2*m) return false;
}
return true;
}
signed main()
{
IOS;
cin>>n>>m;
for(int i=1;i<n;i++) cin>>va[i];
for(int i=1;i<n;i++) sum[i] = sum[i-1]+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;
}
总结:
多多思考哈,把一个复杂的问题去简单化即可。