Problem:2118
Time Limit:3000ms
Memory Limit:665535K
Description
给出两个数集,它们的相似程度定义为Nc/Nt*100%。其中,Nc表示两个数集中相等的、两两互不相同的元素个数,而Nt表示两个数集中总共的互不相同的元素个数。请计算任意两个给出数集的相似程度。
Input
输入第一行给出一个正整数N(N<=50),是集合的个数。随后N行,每行对应一个集合。每个集合首先给出一个正整数M(M<=10^4),是集合中元素的个数;然后跟M个[0, 10^9]区间内的整数。
之后一行给出一个正整数K(K<=2000),随后K行,每行对应一对需要计算相似度的集合的编号(集合从1到N编号)。数字间以空格分隔。
Output
输出共K行,每行一个保留2位小数的实数,表示给定两个集合的相似度值。
Sample Input
3
3 99 87 101
4 87 101 5 87
7 99 101 18 5 135 18 99
2
1 2
1 3
Sample Output
50.00%
33.33%
Source
信息学奥赛课课通
Tips
很容易超时,在疯狂的优化下终于 AC 了。
最开始编译器卡了一下,我以为 set 数组很慢,不能用 set 数组,然后就改成数组排序去重了,结果差不多,正常应该是怎么解都行。
蒟蒻的一些思路:
首先,把所有集合读入数组,读完立刻去重,采用 sort+unique 方式。
然后依次读取要比较的两个集合,由于之前已经排好序了,所以只需要按顺序比较找出两数组中相同元素个数即可。
注:使用 unique 前需要先进行排序。
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int arr[50][10001]; //存储集合中各个元素,多的最后一位存集合中元素个数
double ans[50][50]={0},r; //ans记录之前的查询
int n,num,s1,s2,same,tmp,sp1,sp2;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&arr[i][10000]);
for(int j=0;j<arr[i][10000];j++)
{
scanf("%d",&arr[i][j]);
}
sort(arr[i],arr[i]+arr[i][10000]); //排序
arr[i][10000]=unique(arr[i],arr[i]+arr[i][10000])-arr[i]; //去重
}
scanf("%d",&n);
while(n--)
{
scanf("%d %d",&s1,&s2);
if(ans[s1-1][s2-1]!=0) //已经计算过直接输出
{
printf("%.2f%\n",ans[s1-1][s2-1]);
continue;
}
same=0;
sp1=sp2=0;
while(sp1<arr[s1-1][10000]&&sp2<arr[s2-1][10000]) //比较获取相同元素个数
{
//小的元素指针+1,元素相等same+1
if(arr[s1-1][sp1]<arr[s2-1][sp2])sp1++;
else if(arr[s1-1][sp1]>arr[s2-1][sp2])sp2++;
else
{
same++;
sp1++;
sp2++;
}
}
r=same*100.0/(arr[s1-1][10000]+arr[s2-1][10000]-same); //计算答案
ans[s1-1][s2-1]=ans[s2-1][s1-1]=r; //存储答案
printf("%.2f%\n",r);
}
return 0;
}