题意
给你一个序列,可以选择一个数移到另一个位置,问是否可以满足将序列分成两个非空连续部分后,两部分数之和相等
思路
先统计所有数之和,如果为奇数,显然不行,然后再从左到右求前缀和,如果前缀和正好是总和一半,那显然可以,如果超过了,就需要前部移一个给后部或者后部移一个给前部,先考虑前部移一个给后部,记下第一个超过的位置t,此时要想满足条件就需要从头到t中减掉一个,然后再加上从t+1开始一个连续区间的数字和满足条件,于是我们先把从t+1开始的连续区间的数字和放进一个set,再从头到t枚举减掉哪一个,看剩下需要的是否在set里,如果有就可以了,再考虑后部移一个给前部,其实是一样的方法,只是所有操作都变成了从右往左而已,如果都不行那就是不行了
代码
#include <cstdio>
#include <set>
using namespace std;
long long a[100001];
set<long long> s;
int main()
{
long long n,add,f,sum,t,add2;
scanf("%I64d",&n);
sum=0;
for(long long i=0;i<n;i++)
{
scanf("%I64d",&a[i]);
sum+=a[i];
}
add=0;
f=0;
if(sum%2==1)
printf("NO\n");
else
{
for(long long i=0;i<n;i++)
{
add+=a[i];
if(2*add==sum)
{
f=1;
break;
}
else if(2*add>sum)
{
t=i;
break;
}
}
if(f==1)
printf("YES\n");
else
{
add2=0;
s.insert(add2);
for(long long i=t+1;i<n;i++)
{
add2+=a[i];
s.insert(add2);
}
for(long long i=0;i<=t;i++)
{
if(s.count(sum/2-add+a[i]))
{
f=1;
break;
}
}
if(f==1)
printf("YES\n");
else
{
add2=0;
s.clear();
s.insert(add2);
for(long long i=t-1;i>=0;i--)
{
add2+=a[i];
s.insert(add2);
}
for(long long i=n;i>=t;i--)
{
if(s.count(sum/2-(sum-add+a[t])+a[i]))
{
f=1;
break;
}
}
if(f==1)
printf("YES\n");
else printf("NO\n");
}
}
}
return 0;
}