原题链接:
HDU
题意简述
多组数据。给定一个 n ( n < = 200 ) n(n<=200) n(n<=200),接下来 5 5 5行,每行有 n n n个数(每个数的绝对值 < = 1 0 15 <=10^{15} <=1015),表示 5 5 5个集合。你要求出,能否在 5 5 5个集合中都选一个数,使得和为 0 0 0。
数据
输入
第一行是一个正整数 T T T表示有 T T T组数据。接下来给定 T T T组数据,每个数据有 6 6 6行,先是一行一个 n n n,然后是 5 5 5行,每行 n n n个数。
输出
输出 T T T行。对于每组数据,输出答案。能凑出 0 0 0输出 Y e s Yes Yes,不能输出 N o No No。
样例
输入
2
2
1 -1
1 -1
1 -1
1 -1
1 -1
3
1 2 3
-1 -2 -3
4 5 6
-1 3 2
-4 -10 -1
输出
No
Yes
思路
显然有一个暴力的想法: O ( n 5 ) O(n^5) O(n5)枚举。显然,这个是过不去的。。。
那么,如果我们能枚举一半,然后用记录的方法直接求得另一半呢?
如果是第一次做这种题应该很难能想到有记忆化的方法。不过,能做出来这种题,要么就是你天才,第一眼看到就能做出来;要么就是你努力,做过类似的题,然后也能像天才一样一眼看出来怎么做。
我们有这样一个记忆化的思路:枚举第 2 , 3 2,3 2,3个集合中所有的和,记录到 v 1 v1 v1。枚举第 4 , 5 4,5 4,5个集合中所有的和,记录到 v 2 v2 v2。然后,我们只要枚举第一个集合,再枚举 v 1 , v 2 v1,v2 v1,v2中的元素,判断是否和为 0 0 0。
珂是, v 1 , v 2 v1,v2 v1,v2总各有 n 2 n^2 n2个元素,这样枚举,不还是 O ( n ∗ n 2 ∗ n 2 ) = O ( n 5 ) O(n*n^2*n^2)=O(n^5) O(n∗n2∗n2)=O(n5)的么?
我们会发现, v 1 , v 2 v1,v2 v1,v2中的元素,顺序不重要,怎么排列都不会影响。所以,我们令 v 1 , v 2 v1,v2 v1,v2都升序排列。设枚举的第一个元素中的数是 a a a, v 1 v1 v1中枚举的是 b 1 b1 b1, v 2 v2 v2中枚举的是 b 2 b_2 b2。当我们不断向后枚举 b 1 b1 b1的时候, b 1 b1 b1是递增的,而且此时 a a a没有变,那么不难证明,如果有满足条件的 b 2 b2 b2,那么位置一定不会更后,一定会往前。
所以我们只要记录好我们枚举到 v 2 v2 v2的哪个位置,每次 − − -- −−即珂,而不是重新找。这样就 O ( n 2 l o g n + n 3 ) O(n^2logn+n^3) O(n2logn+n3)了。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define int long long
#define N 40100
int v1[N];int p1=0;
int v2[N];int p2=0;//v1和v2,p1和p2表示用到了哪个位置
int n;
int a[6][N];//5个集合
void Input()
{
scanf("%lld",&n);
for(int i=1;i<=5;++i)
{
for(int j=1;j<=n;++j)
{
scanf("%lld",&a[i][j]);
}
}
}
void Build()
{
p1=p2=0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
++p1;
v1[p1]=a[1][i]+a[2][j];
++p2;
v2[p2]=a[3][i]+a[4][j];
}
}
sort(v1+1,v1+p1+1);
sort(v2+1,v2+p2+1);//记得排序
}
void Solve()
{
int ans=0;
for(int i=1;i<=n;++i)
{
int pos=p2;//记录v2用到了哪个位置
for(int j=1;j<=p1 and pos>=1;++j)
{
while(a[5][i]+v1[j]+v2[pos]>0) --pos;
//不断往前找,因为显然pos往后+是没有答案的
if (a[5][i]+v1[j]+v2[pos]==0)
{
ans=1;//如果找到了就记录答案
}
}
}
printf("%s\n",ans==1?"Yes":"No");
}
void Main()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
int t;scanf("%lld",&t);
while(t--)
{
Input();
Build();
Solve();
}
}
};
main()
{
Flandle_Scarlet::Main();
return 0;
}