TYWZOJ #10223.四元组 题解(胎教级题解)

题目来源

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值