友好数对(kompici)

友好数对(kompici)

题目描述

在顺利完成家庭作业以后,Mirko感到非常的厌倦。所以,他列出了N个数,这些数中有些数对他是喜欢的,有些数对他是不喜欢的。
他喜欢的数对叫做友好数对,如果两个数至少有一个相同的数字(不要求在相同的位置),那么这两个数就是友好数对。请帮助Mirko在这N个数找出有多少友好数对。

输入

第一行一个正整数N(1<=N<=1000000)。
接下来N行,每行一个正整数,范围在1到1018之间。N个数中任意两个数都是不同的。

输出

只有一行一个整数,表示友好数对的个数。

在这里插入图片描述

这道题正解是用容斥原理+位运算

当然正解也是靠慢慢得部分分得出的。

20~40%
对于20~40分,我们采用最普通的暴力,我们用双重循环来循环暴力每两个数。
对于a[i],a[j] (a[ ]表示我们输入的数),我们将它们每一位都进行分解,然后看看是否有相同的数,如果有则ans++,没有则继续进行循环。

code:

#include<cstdio>
#include<iostream>
using namespace std;
long a[1000010],b[20],c[20];
int main()
{
	freopen("kompici.in","r",stdin);
    freopen("kompici.out","w",stdout);
	long long n,ans=0;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			long long t=a[i],tt=a[j],te1=0,te2=0;
			while(t!=0)
			{
				b[++te1]=t%10; 
				t/=10;
			}
			while(tt!=0)
			{
				c[++te2]=tt%10; 
				tt/=10;
			}
			bool f=false;
			for(int _=1;_<=te1;_++)
			{
				for(int __=1;__<=te2;__++)
				{
					if(b[_]==c[__])
					{
						ans++;
						f=true;
						break;
					}
				}
				if(f==true) break;
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

100%
我们使用容斥原理+位运算
对于我们每次输入的数,我们自己定义一个二进制数(九位二进制数),
然后我们在将它转为十进制,桶++,桶的范围是1024,
因为我们我们最大也就是每位都是1(2的10次方-1)。
举例来说:
假设这个数是23
那我们的二进制数则是1100

why
先讲一下我们自己定义的二进制数:
这个数有我们原先输入的数转变而成。我们
将原先的数每一位进行分解,每次分解的数(设它为t),
然后t与二进制的第t+1进行或运算,为什么是t+1,
因为我们要考虑0.之所以用或运算,因为题目只规定了判断两个数之间是否有相同数,并没要你求有几个相同。例如:4 和 44,只让你求他们的相同数是4,没让你求有多少个。
OK,我们讲完预处理接着将容斥操作。
我们用双重循环来进行遍历
for i=1;i<=1023;i++
for j=1;j<=1023;j++
然后,我们判断i与j是否有相同的数
并我们输入的有这个数,重中之重还要
判断i!=j.
if(i&j && b[j]!=0 && i!=j)
然后条件如果成立
则将他们两的出现次数乘积加到ans中
这样就完了吗?
不可能。我们按照老方法,举个栗子。
4和44,尽管他们都化为16(10000)并加了出现次数,但是他们
还可以进行一次匹配,所以每次i循环末都要将答案加b[i]*b[i-1];
最后输出答案(记得除以二哦!

我讲明白了吗?

code:

#include<iostream>
#include<cstdio>
using namespace std;
long long n,b[1024],k,ans,s,x;
int main()
{
	freopen("kompici.in","r",stdin);
    freopen("kompici.out","w",stdout);
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%lld",&x);
        //预处理
        s=0;
        while(x!=0)
        {
            k=x%10;
            x=x/10;
            s=s|(1<<k);
        }
        b[s]++;
    }
    //容斥操作
    for(int i=1; i<=1023; i++)
    {
        for(int j=1; j<=1023; j++)
            if(i&j&&b[j]!=0&&i!=j) ans=ans+b[i]*b[j];
         ans=ans+b[i]*(b[i]-1);
    }
    printf("%lld",ans/2);  
    //为什么要除二,因为i之前是5j是55,而后面i可以变成55j变成5,故要除2!
	return 0;
}

谢谢

发布了80 篇原创文章 · 获赞 58 · 访问量 2525

猜你喜欢

转载自blog.csdn.net/bigwinner888/article/details/104431407
今日推荐