题目链接:https://codeforces.com/problemset/problem/1692/E

前缀和 + 二分  或   双向队列双指针

大意:输入n个数,只包含 0 和1,输入一个数s。sum为数列的和,每次操作可以删除第一个数或最后一个数,最后求最少通过几次这样的操作可以使得sum=s,如果无法实现输出 -1。

方法:这个题十分巧妙,里面用来确定mid 的 l,仅仅起到了控制范围的作用,而真正的sum的左边界是循环的 i  这样做的复杂度就是 nlogn了

这个题把我整懵的地方就是要找到两个合适的边界,我只想到了枚举。虽然题目给出了二分的类别,但是还是没明白怎么二分

后来想了想,这题其实并不难,我们可以暴力一个边界,另一个边界通过强秒的方法来计算出来,而不需要首位两两对应一遍


前缀和+二分代码:


#include<bits/stdc++.h>
using namespace std;
int a[100000*2+10];
int sq[100000*2+10];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,s,sum=0;
		scanf("%d%d",&n,&s);
		memset(a,0,sizeof a);
		memset(sq,0,sizeof sq);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			sq[i]=sq[i-1]+a[i];
		}
		int mint = 1e9;
		for(int i=1;i<=n;i++)//以i作为一个起点 
		{
			int l = i, r = n;
			while(l<=r)
			{
				int mid = l+r>>1;
				if(sq[mid]-sq[i-1] <=s)//如果  mid 减去  l前的  <=s,那么我们就往后推导,增加mid的值 
				{
					l = mid +1;//l  仅仅用来控制距离,我们最终需要的是  i  - j这个范围的sum 
				}else//如果 mid 减去 l前的  >s,那么我们就往前推导,这么mid的位置靠前,减少mid的值 
				{
					r = mid -1;
				}
			}
			if(sq[r]-sq[i-1]==s)
				mint = min(mint,n-(r-i+1));
		}
		printf("%d\n",mint<1e9?mint:-1);
	}
} 
双向队列指针代码(没get到)感觉和上一个二分差不多,只不过这里也算暴力了,就当作是另一种写法吧:



//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>

#define int long long
#define double long double

void solve() {
    int n, s;
    std::cin >> n >> s;

    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
        a[i] += a[i - 1];
    }

    int ans = 1e9;

    int r = 1;
    for (int i = 1; i <= n; ++i) {
        while (r < n && a[r + 1] - a[i - 1] <= s) r++;
        if (a[r] - a[i - 1] == s) 
        	ans = std::min(ans, n - (r - i + 1));
    }
    std::cout << (ans < 1e9 ? ans : -1) << "\n";
}


signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int tt(1);
    std::cin >> tt;
    
    while (tt--) {
        solve();
    }
    return 0;
}
最后是我的笨拙的
#include<bits/stdc++.h>
using namespace std;
int a[100000*2+10];
int sq[100000*2+10],sh[100000*2+10];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,s,sum=0;
		scanf("%d%d",&n,&s);
		memset(a,0,sizeof a);
		memset(sq,0,sizeof sq);
		memset(sh,0,sizeof sh);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			sum += a[i];
			sq[i]=sq[i-1]+a[i];
		}
		for(int i=n;i>=1;i--)
		{
			sh[i]=sh[i+1]+a[i];
		}
		//  前后缀和  
		int cha = sum - s;
		if(cha==0)
		{
			printf("0\n");
		}else if(cha<0)
		{
			printf("-1\n");
		}else
		{
			int mint = 2*100000+10;
			for(int i = 0;i<=n;i++)
			{
				if(sq[i]>cha)
					break;
				for(int j = n+1;j>i;j--)
				{
					if(sh[j]>cha)
						break;
					if(sq[i]+sh[j] == cha)
					{
						mint=min(mint,i+n-j+1);
					}else if(sq[i]+sh[j]>cha)
					{
						break;
					}
				}
			}
			printf("%d\n",mint);
		}
		
	}
} 
暴力写法(前缀和+后缀和,相加计算差值):