题目来源
TYWZOJ 10223题 四元组
输入文件:zigzags.in
输出文件:zigzags.out
题目描述
给出n个数a1,a2...,an ,求问有多少个四元组(i,j,k,l),使得这个四元组满足下列条件:
- i<=i<j<k<l<=n;
- ai=ak并且aj=al。
-
输入格式
第一行输入一个数n,表示有多少个数。 对于每组数据,第二行输入n个数,表示a1,a2,...an。
输出格式
一个整数,代表合法四元组的个数。
-
输入 #1
6 1 3 3 1 2 3
输出 #1
2
输入 #2
5 2 2 2 2 2
输出 #2
5
数据范围
对于50% 的数据,有1<=n<=10^2。
对于100%的数据,有1<=n<=5000,1<=ai<=n 。
解法分析
这道题,很显然最直接想到的思路是通过枚举四元组中的某几个元素来计数。刚开始我以为这四元组必须放在一起。但是如果你用这个思路去试一下样例1,你就会发现答案为0。毕竟善良的老师怎能随便放过我们呢?
之后,我再一次分析题意,确定四元组是可以分开的。而很多同学刚开始直接去暴力枚举,但这很显然会TLE。
而我们的四元组只要符合i<=i<j<k<l<=n,ai=ak并且aj=al即可。所以我们可以枚举每一位的位置,寻找两数的下标的差值大于等于1,并且两数相等的“二元组”,之后将“二元组”尝试两两配对,组成“四元组”,最后输出“四元组”的个数,本题就AC了。
难点分析
1.如何求出二元组:
可以套两层循环,外层循环i从1循环到n,内层循环j从i+2循环到n(我这里的在循环时能保证i的值小于j,可以简化空间复杂度。而j的值刚开始直接变成i+2,就不需要判断j和i的差是否大于等于i),之后当我们找到一个合法的“二元组”之后,由于我们并不关心具体数值是多少,所以我们开一个二维数组s[2][tot](tot代表第tot组“二元组”,s[0][tot]和s[1][tot]分别代表两个数的下标),直接用于存放“二元组”的下标。在两层循环结束后,就可以求出“二元组”的个数(即tot的值),tot在后面的运行过程中还要发挥重要的作用。参考代码如下:
for(int i=1;i<=n;i++){
for(int j=i+2;j<=n;j++){
if(a[i]==a[j]){
++tot;
s[0][tot]=i;
s[1][tot]=j;
}
}
}
【tips:“二元组”的个数最多时即为a1到an均为同一个数的情况,此时tot的值为[(n-2)+(n-3)+...+2+1],即(n-2)*(n-1)/2,因此可以在初始化可以开成s[2][(n-2)*(n-1)/2]】
2.如何将若干组二元组两两组合:
在组合时,我们枚举二元组两两组合的所有情况,即套两层循环,外层循环i从1循环到tot-1(tot为“二元组”的组数),内层循环j从i+1循环到tot。此时,第一个“二元组”的两个数的下标分别为:s[0][i],s[1][i],第二个“二元组”的两个数的下标分别为:s[0][j],s[1][j],在组合时,两个“二元组”会有两种组合方式,顺序依次为:s[0][i],s[0][j],s[1][i],s[1][j]或s[0][j],s[0][i],s[1][j],s[1][i](即将第二个“二元组”中的两数插到第一个“二元组”的两数中)。这时我们一定要清楚:前面已经通过条件限制是的每一个“二元组”的两个数相等,所以这里直接判断四个数的下标是否满足s[0][i]<s[0][j]<s[1][i]<s[1][j]或s[0][j]<s[0][i]<s[1][j]<s[1][i]即可。我们可以建一个变量sum,在发现一个可以组合成功的“四元组”之后,将sum的值加1。参考代码如下:
int sum=0;
for(int i=1;i<tot;i++){
for(int j=i+1;j<=tot;j++){
if(s[0][i]<s[0][j]&&s[0][j]<s[1][i]&&s[1][i]<s[1][j])
sum++;
if(s[0][j]<s[0][i]&&s[0][i]<s[1][j]&&s[1][j]<s[1][i])
sum++;
}
}
完整参考代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5000;
int a[N+5];
int s[2][(N-2)*(N-1)/2+5];
int main(){
//freopen("zigzags.in","r",stdin);
//freopen("zigzags.out","w",stdout);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int tot=0;
for(int i=1;i<=n;i++){
for(int j=i+2;j<=n;j++){
if(a[i]==a[j]){
++tot;
s[0][tot]=i;
s[1][tot]=j;
}
}
}
int sum=0;
for(int i=1;i<tot;i++){
for(int j=i+1;j<=tot;j++){
if(s[0][i]<s[0][j]&&s[0][j]<s[1][i]&&s[1][i]<s[1][j])
sum++;
if(s[0][j]<s[0][i]&&s[0][i]<s[1][j]&&s[1][j]<s[1][i])
sum++;
}
}
printf("%d",sum);
return 0;
}