Codeforces#799(Div4)E. Binary Deque

Slavic has an array of length n consisting only of zeroes and ones. In one operation, he removes either the first or the last element of the array.

What is the minimum number of operations Slavic has to perform such that the total sum of the array is equal to ss after performing all the operations? In case the sum s can't be obtained after any amount of operations, you should output -1.

Input

The first line contains a single integer t (1≤t≤10^4) — the number of test cases.

The first line of each test case contains two integers n and s (1≤n,s≤2*10^5) — the length of the array and the needed sum of elements.

The second line of each test case contains n integers ai (0≤ai≤1) — the elements of the array.

It is guaranteed that the sum of nn over all test cases doesn't exceed 2*10^5.

Output

For each test case, output a single integer — the minimum amount of operations required to have the total sum of the array equal to s, or -1 if obtaining an array with sum s isn't possible.

_____________________________________________________________________________

斯拉夫语有一长为n的数组仅由零和一组成。在一个操作中,他删除了数组的第一个或最后一个元素。

斯拉夫语必须执行的最小操作次数是多少,使得数组的总和等于s?如果总和s任何数量的操作后都无法获得,您应该输出 -1。

输入

第一行包含单个整数tt (1≤t≤10^4) — 测试用例的数量。

每个测试用例的第一行包含两个整数ns (1≤n,s≤2*10^5) — 数组的长度和所需的元素总和。

每个测试用例的第二行包含n个整数ai (0≤ai≤1) — 数组的元素。

保证n在所有测试用例不超过2*10^5

输出

对于每个测试用例,输出一个最小操作数使数组总和等于s,如果得不到输出-1。

———————————————————————————————————————————

Input

7
3 1
1 0 0
3 1
1 1 0
9 3
0 1 0 1 1 1 0 0 1
6 4
1 1 1 1 1 1
5 1
0 0 1 1 0
16 2
1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 1
6 3
1 0 1 0 0 0

Output

0
1
3
2
2
7
-1

———————————————————————————————————————————

  1. 解法一:

利用前缀和与后缀和暴力求解。在能够使数组总和等于s的情况下,用两个数组分别存储数组的前缀和与后缀和,在输数组时记录数组总和sum,就可以利用二分查找找前缀和与后缀和相加等于sum-s了,找出其中删除数据最少的那个(即前缀和与后缀和下标相加最小的)。

Tips:

  • 1.遍历前缀和数组时,在后缀和数组中用二分查找sum-s-q[i]时,右下标不是从n开始的,而是n-i,因为一个数不可以删两次
  • 2.二分查找也不是查找到sum-s-q[i]就返回,因为有0的存在,可能查找的那个中间值前面有0,如果之间返回的话就表示把0也删掉了,就不是最小操作数了。
  • 3.sum-s-q[i]要保证大于0,等于0就表示不需要从后边删了,直接就在前面删完了,也不需要再往后遍历前缀和数组了,因为前缀和数组一定是不下降的。
  • (l+r)>>1表示除以2,取整
  • (l+r)<<1表示乘以2
  • #include<iostream>
    #include<cstring>
    using namespace std;
    const int M = 2e5 + 5;
    int n, s;
    int a[M];
    int q[M];//前缀和数组
    int h[M];//后缀和数组
    //q[i]+h[j]==s
    //并且找到的h[j]的j要是所有可能性中最小的那个,代表删除的数据最少
    //注意前缀和删过的数后缀和数组不可以再删了
    int BinarySearch(int target, int i) {
        int l = 1, r = n - i, mid;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (h[mid] < target) {
                l = mid + 1;
            }
            else {
                r = mid - 1;
            }
        }
        return l;
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        int t, i, p, k, j;
        cin >> t;
        while (t--) {
            memset(q, 0, sizeof(a));
            memset(h, 0, sizeof(a));
            cin >> n >> s;
            int sum = 0, min = n + 1;
            for (i = 1; i <= n; i++) {
                cin >> a[i];
                sum += a[i];
            }
            if (sum < s) {
                cout << "-1" << endl;
                continue;
            }
            if (sum == s) {
                cout << "0" << endl;
                continue;
            }
            s = sum - s;
            for (i = 1; i <= n; i++) {
                q[i] = q[i - 1] + a[i];
                h[i] = h[i - 1] + a[n - i + 1];
            }
            for (i = 1; i <= n; i++) {
                //注意返回左下标可能会存在返回n-i+1的情况
                //如果q[i]==0,说明可以不删,h[p]==0,说明也可以不删
                if (q[i] == s) {
                    min = (i < min ? i : min);
                    break;
                }
                p = BinarySearch(s - q[i], i);
                if (p <= n - i && h[p] == s - q[i]) {
                    p = (h[p] == 0 ? 0 : p);
                    j = (q[i] == 0 ? 0 : i);
                    k = p + j;
                    min = (k < min ? k : min);
                }
            }
            cout << min << endl;
        }
        return 0;
    }

    ———————————————————————————————————————————

  • 解法二:

  • 从数组中删除最少的元素使数组的和变为s其实也就是在数组中找一段最长的和为s的连续子序列

  • 这种方法要小心超时,主要就是要想清楚怎么减少重复计算的情况,如果从第一个数开始往后加到s,计算当前个数,然后又从第二个数开始重新往后加……这样一定会超时,并且他们之间的一部分数字实际上是经过了多次计算的。

  • 所以我们可以先从第一个数开始找到s,在它继续往后加到s+1时,前面就不应该再是从第一个数开始了,我们把开始的位置右移,直到和再次变为s,然后又继续往后加,以此类推。

  • #include<iostream>
    using namespace std;
    const int M = 2e5+5;
    int a[M];
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int t,n,s,i;
        cin>>t;
        while(t--){
            cin>>n>>s;
            int sum=0,ans=-1;
            for(i=1;i<=n;i++){
                cin>>a[i];
                sum+=a[i];
            }
            if (sum < s) {
                cout << "-1" << endl;
                continue;
            }
            if (sum == s) {
                cout << "0" << endl;
                continue;
            }
            //从j开始找和为s的子序列,p记录子序列长度
            //前面的数据减少,后面的数据增加,中间保留
            int j=1,p,k=0;
            for(i=1;i<=n;i++){
                k+=a[i];
                while(k>s){
                    k-=a[j];
                    j++;
                }
                if(k==s){
                    p=i-j+1;
                    ans=(p>ans?p:ans);
                }
            }
            cout<<n-ans<<endl;
        }
        return 0;
    }
    

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值