传送门:PAT (Advanced Level) Practice 1063 Set Similarity / NEFU 2118 相似的数集
思路(3种)
多次询问两个序列的交集个数/并集个数的值,一定先记忆化处理,即之前询问过的答案记录下来,下次再询问就直接输出答案。
对于求交集个数,3种思路:
(1) 可以用set的set_intersection直接求。
(2) unordered_set手动for循环计数。
(3) 用数组去重后,手动计算交集个数。
AC代码(1),使用set_intersection函数
将set集合a,b求交集,存于set集合t,函数用法:
set_intersection(a.begin(),a.end(),b.begin(),b.end(),inserter(t,t.begin()));
#include <bits/stdc++.h>
using namespace std;
const int N=52;
double dp[N][N]; // 记忆化
set<int>a[N],t;
int main()
{
ios::sync_with_stdio(false);
int n,m,x,y,q;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>m;
while(m--)
{
cin>>x;
a[i].insert(x);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)dp[i][j]=1;
else dp[i][j]=-1;
cin>>q;
while(q--)
{
cin>>x>>y;
if(dp[x][y]!=-1){printf("%.1f%%\n",dp[x][y]);continue;}
t.clear();
set_intersection(a[x].begin(),a[x].end(),a[y].begin(),a[y].end(),inserter(t,t.begin())); // 求a[x],a[y]交集,存于t
int cnt1=t.size();
int cnt2=a[x].size()+a[y].size()-cnt1;
printf("%.1f%%\n",dp[x][y]=dp[y][x]=100.0*cnt1/cnt2); // 记忆化
}
return 0;
}
AC代码(2),unordered_set手动循环计数
#include <bits/stdc++.h>
using namespace std;
const int N=52;
double dp[N][N]; // 记忆化
unordered_set<int>a[N];
int main()
{
ios::sync_with_stdio(false);
int n,m,x,y,q;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>m;
for(int j=1;j<=m;j++)
{
cin>>x;
a[i].insert(x);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)dp[i][j]=1;
else dp[i][j]=-1;
cin>>q;
while(q--)
{
cin>>x>>y;
if(dp[x][y]!=-1)printf("%.1f%%\n",dp[x][y]);
else
{
int cnt1=0;
for(auto it:a[x])
if(a[y].find(it)!=a[y].end())cnt1++;
int cnt2=a[x].size()+a[y].size()-cnt1;
printf("%.1f%%\n",dp[y][x]=dp[x][y]=100.0*cnt1/cnt2); // 记忆化
}
}
return 0;
}
AC代码(3),数组手动模拟(速度最快!)
// 最快的速度
#include <bits/stdc++.h>
using namespace std;
const int N=52,M=1e4+10;
int n,m,x,y,q,t[M],len[N],a[N][M];
double dp[N][N]; // 记忆化
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>m;
for(int j=1;j<=m;j++)cin>>t[j];
sort(t+1,t+m+1);
len[i]=unique(t+1,t+m+1)-t-1;
for(int j=1;j<=len[i];j++)a[i][j]=t[j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)dp[i][j]=1;
else dp[i][j]=-1;
cin>>q;
while(q--)
{
cin>>x>>y;
if(dp[x][y]!=-1){printf("%.1f%%\n",dp[x][y]);continue;} // 记忆化
int p1=1,p2=1,cnt1=0;
while(p1<=len[x]&&p2<=len[y])
{
if(a[x][p1]<a[y][p2])p1++;
else if(a[x][p1]>a[y][p2])p2++;
else p1++,p2++,cnt1++; // 求交集个数
}
int cnt2=len[x]+len[y]-cnt1; // 求并集个数
printf("%.1f%%\n",dp[x][y]=dp[y][x]=100.0*cnt1/cnt2); // 记忆化
}
return 0;
}