原题链接

题目描述

给定一个长度为 nn 的正整数数组 a1,a2,,ana_1,a_2,…,a_n

你需要选择其中一个元素,将其移动至数组中的任意位置(也可以留在原位置)。

我们的目标是,在移动元素操作完成以后,将数组分为前后两个非空部分,并使前一部分的各元素之和等于后一部分的各元素之和。

请问,该目标能否达成?

输入格式

第一行包含整数 TT,表示共有 TT 组测试数据。

每组数据第一行包含整数 nn

第二行包含 nn 个整数 a1,a2,,ana_1,a_2,…,a_n

输出格式

每组数据输出一行结果,目标可以达成,则输出 YES,否则输出 NO

数据范围

1T201≤T≤20,
1n1051≤n≤10^5,
1ai1091≤a_i≤10^9
同一测试点内所有 nn 的和不超过 10510^5

样例

输入样例:

3
3
1 3 2
5
1 2 3 4 5
5
2 2 3 4 5

输出样例:

YES
NO
YES

思路

能否达成,可以分三种情况来讨论。

假设 SiS_i 是前 ii 个数的和

  1. 存在某一个前缀和 Si=Sn2S_i = \frac{S_n}{2} 。不需要移动。

  2. 存在某一个后缀和 Si=Sn2S_i = \frac{S_n}{2} 。不需要移动。

  3. 需要移动的情况下。来看以 ii 结尾部分 a1,a2,,aia_{1}, a_{2}, \ldots, a_i 。如果以 ii 结尾的前缀和减去 aka_{k} 等于 Sn2\frac{S_n}{2} 的话,是不是就相当于给 aka_{k} 移动到 ii 后面去了,就相当于 Siak=Sn2S_i - a_{k} = \frac{S_n}{2} ,这里 SiS_iSnS_n 都是已知的,整理一下可以得到 ak=SiSn2a_{k} = S_i - \frac{S_n}{2} 。判断一个数是否存在可以用哈希表来做。

细节:

  1. 后缀和就是从后面开始的前缀和。多开一个数组存逆序的数组。

  2. 如果总和为奇数肯定不可能。

  3. 如果是第 11 种或第 22 种情况,只要在哈希表里插入一个 00 即可。

代码

C++

#include <iostream>
#include <unordered_set>

using namespace std;

typedef long long LL;

const int N = 1e5 + 5;
int n, a[N], b[N];
LL s[N];

bool check(int w[])
{
    for ( int i = 1; i <= n; i ++ ) 
        s[i] = s[i - 1] + w[i];
    if ( s[n] % 2 ) return false;
    unordered_set<LL> S;
    S.insert(0);
    for ( int i = 1; i <= n; i++ )
    {
        S.insert(w[i]);
        if ( S.count(s[i] - s[n] / 2) ) return true;
    }
    return false;
}

int main()
{
    int T;
    cin >> T;
    while ( T -- )
    {
        cin >> n;
        for ( int i = 1, j = n; i <= n; i ++, j -- )
        {
            cin >> a[i];
            b[j] = a[i];
        }
        if ( check(a) || check(b) ) puts("YES");
        else puts("NO");
    }
    return 0;
}

Java

import java.util.*;

public class Main
{
    private static Scanner in = new Scanner(System.in);
    private static final int N = (int) (1e5) + 5;
    private static int n, a[] = new int[N], b[] = new int[N];
    private static long s[] = new long[N];
    
    public static void main(String args[])
    {
        int T = in.nextInt();
        while ( T -- > 0 )
        {
            n = in.nextInt();
            for ( int i = 1, j = n; i <= n; i ++, j -- )
            {
                a[i] = in.nextInt();
                b[j] = a[i];
            }
            System.out.printf("%s\n", check(a) || check(b) ? "YES" : "NO");
        }
    }
    
    private static boolean check(int w[])
    {
        for ( int i = 1; i <= n; i ++ )
            s[i] = s[i - 1] + w[i];
        if ( s[n] % 2 == 1 ) return false;
        Set<Long> S = new HashSet<>();
        S.add(0L);
        for ( int i = 1; i <= n; i ++ )
        {
            S.add((long) w[i]);
            if ( S.contains(s[i] - s[n] / 2) ) return true;
        }
        return false;
    }
}