題目意思:
給5組數,每組含n個數(n<=200),在5組數中各選一個,問是否存在一種組合使這5個數的和為0。
解題思路:
這題數據很強,o(n^3*lgn過不了)
對於在兩組數各選一個使得和為定值,有一種貪心的算法,時間復雜度為(n*lgn,實際上就是排序的算法時間復雜度)。
所以可以把第二組和第三組數合並組合40000個數作為第二組,第四組和第五組合並成40000個數作為第三組。
分別排序。
1、然後枚舉第一組,初始時用p指針指向第二組中的第一個數,q指針指向第三組的最後一個數,以第二組為基准。
2、當三數大於0時,說明q指針後面的所有數與p指針後面的數肯定不行,大於當前的和值,同時說明p指針後面的只可能和q指針前面的湊,所以q指針往前移。
3、當三數小於0時,說明p與當前的q不行,p與q後面的也不行(由上一步得),p與q前面的更不行。所以p只能往後移。
貪心思想,所以最多只移動了40000+40000次。
所以總的時間復雜度為o(n^3).
代碼:
<SPAN style="FONT-SIZE: 18px">#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF 0x1f1f1f1f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
/*
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
*/
ll s1[41000],s2[41000];
ll sa[6][210];
int cnt1,cnt2,n;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=5;i++)
for(int j=1;j<=n;j++)
scanf("%I64d",&sa[i][j]);
//sort(sa[1]+1,sa[1]+n+1);
cnt1=cnt2=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
s1[++cnt1]=sa[2][i]+sa[3][j]; //把2、3組合並和把4、5組合並
s2[++cnt2]=sa[4][i]+sa[5][j];
}
sort(s1+1,s1+cnt1+1);
sort(s2+1,s2+cnt2+1);
int t=1; //離散化一下,去掉相同的
for(int i=2;i<=cnt1;i++)
if(s1[i]!=s1[i-1])
s1[++t]=s1[i];
cnt1=t;
t=1;
for(int i=2;i<=cnt2;i++)
if(s2[i]!=s2[i-1])
s2[++t]=s2[i];
cnt2=t;
/* if(sa[1][n]+s1[cnt1]+s2[cnt2]<0)
{
printf("Yes\n");
continue;
}*/
bool ans=false;
for(int i=1;i<=n&&!ans;i++)
{
//if(sa[1][i]+s1[1]+s2[1]>0)
// break;
for(int p=1,q=cnt2;p<=cnt1&&q>=1;) //貪心思想
{
ll tt=sa[1][i]+s1[p]+s2[q];
if(tt==0)
{
ans=true;
break;
}
else if(tt>0)
q--;
else
p++;
}
}
if(ans)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
</SPAN>