题意:
就是给你一个n长度的数组,和本金m,然后在这个数组中找出最长的一段连续区间,在这段区间中当前的权值加上本金m没有<0的时候,求最长的区间。
思考:
看到这题的时候已经没有几分钟了,一看题意很简单,就是求最长的,赶紧想了想,这不就是双指针吗,但是调了调没调出来,又去想二分了,对于每个i点枚举它的最远点,但是这是错误的,这只能保证最远点当前的值是正的,但是中间可能会出现负值,所以这样不行。那该怎么做呢,其实用线段树维护前缀和数组的最小值,这样每次查询最远点的时候,就求这段区间的最小值是多少,如果最小值都满足条件,那么这个区间就满足条件。当然双指针是很简单的,就是如果可以的话就让r++,然后不能加就出来,更新最大值,然后l++。
代码:
线段树版本:
struct Node{
int L,R;
int sum;
}t[4*N];
int T,n,m;
int va[N];
void build(int node,int l,int r)
{
t[node].L = l,t[node].R = r;
if(l==r)
{
t[node].sum = va[l];
return ;
}
int mid = l+r>>1;
build(node_l,l,mid);
build(node_r,mid+1,r);
t[node].sum = min(t[node_l].sum,t[node_r].sum);
}
int query(int node,int l,int r)
{
if(t[node].L>r||t[node].R<l) return inf;
if(t[node].L>=l&&t[node].R<=r) return t[node].sum;
return min(query(node_l,l,r),query(node_r,l,r));
}
bool check(int l,int r)
{
int res = query(1,l,r)-va[l-1]; //由于维护的是前缀和,求出来之后要减去之前的。
if(res+m>=0) return true;
else return false;
}
signed main()
{
IOS;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
va[i] = va[i-1]+x;
}
build(1,1,n);
int maxn = 0,vall = 0,valr = 0;
for(int i=1;i<=n;i++)
{
int l = i,r = n;
while(l<r)
{
int mid = (l+r+1)>>1;
if(check(i,mid)) l = mid;
else r = mid-1;
}
if(maxn<l-i+1&&check(i,l)) //注意这里,maxn<l-i+1,该是多少就多少,别l-i
{
maxn = l-i+1;
vall = i,valr = l;
}
}
if(vall==0||valr==0) cout<<-1<<"\n";
else cout<<vall<<" "<<valr<<"\n";
}
return 0;
}
双指针版本:
int T,n,m;
int va[N];
int dp[N];
signed main()
{
IOS;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>va[i];
int now = 0,maxn = 0,l = 1,r = 1,vall = 0,valr = 0;
while(l<=n&&r<=n)
{
while(m+now+va[r]>=0&&r<=n) now += va[r++]; //如果可以r++
if(maxn<r-l) //更新
{
maxn = r-l;
vall = l,valr = r-1;
}
now -= va[l++]; //l++
}
if(vall==0&&valr==0) cout<<"-1\n";
else cout<<vall<<" "<<valr<<"\n";
}
}
总结:
怎么说呢,对双指针要加强练习和理解,知道该干什么,该怎么做。