[codeforces] Binary Deque
题意:
二进制双端队列,数组里的数是0,1,每一次删除可以选择最后一个或第一个,问最少操作几次后得到要求的数s,如果怎么操作都没法得到s,输出-1。
可以这样想,我操作总次数可以看作是对左端和右端操作的次数的和,这个就相当于一个滑动窗口
以第三个例子为例
像这种滑动窗口要通过维护一段子列来解决,我们用双指针法来解决。(这里的指针更像是一种标记点,符合题意标记点移动)
sum为和,l,r为左右指针,初始值为1
sum=s时,更新答案,并且我们r指针向右移动
sum<s时,r指针向右移动,使sum尽可能增大
sum>s时,l指针向右移动,使sum尽可能减小
直到l,r中某个越界为止
#include<bits/stdc++.h>
using namespace std;
int t;
int n, s;
const int N = 2e5 + 5;
int a[N];
int main()
{
cin >> t;
for (int i = 0; i < t; i++)
{
cin >> n >> s;
int sum = 0;int ans = 0x3fffffff;//0x3fffffff是代表无穷大的意思,因为取最小值,所以初始数大一点,而且注意不能搞成全局变量,因为有很多测试数据,要保证每一次的ans初始为无穷大
for (int j = 1; j <= n; j++)
{
cin >> a[j];
sum += a[j];
}
if (sum == s) { cout << 0 << endl; }//总和等于s,肯定不用操作,因为有操作就不是最小了
else if (sum < s) { cout << -1 << endl; }//总和小于s,直接输出-1得了,因为本题的操作后的数的总和本来就是会变小的
else
{
sum = a[1];
int l = 1, r = 1;
while (l <= n && r <= n)
{
if (sum == s)
{
ans = min(ans, n - r + l - 1);
r++;
sum += a[r];
}
else if (sum < s)
{
r++;
sum += a[r];//这里是移动右指针,是要把右边的数加上,只能移动后才能加
}
else
{
sum -= a[l];//这里的逻辑是左指针向右移动,它要使sum变小,应该要把左边的数减掉,所以先减再移动指针
l++;
}
}
cout << ans << endl;
}
memset(a, 0, sizeof(a));//初始化数组为0,不然有一部分数还残余在里面,影响后面计算
}
return 0;
}