传送门: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;
}