PAT1063 Set Similarity(测试点4超时)set使用

22 篇文章 0 订阅
13 篇文章 1 订阅

原题链接1063 Set Similarity

这道题挺简单的,就是给好几个数列,每个数列里面有可能有重复元素,再给定几个查询,每个查询给了两个数列编号(从1开始),问在这两个数列中都出现的数的个数nc 占 两个数组中的去掉重复数据后的数的个数nt的百分比。
比如说

99 87 101
87 101 5 87

这两个数组中,重复出现的元素为87,101,故nc = 2。而所有数据为5、87、99、101,即nt = 4,所以占比为50.0%。
因为原数列中就可能包含有重复元素,所以使用unordered_set < int >来存储所有数列,这样原数组中的重复元素便只保留一个。
之后对于每次查询,使用unorder_map < int,bool > vis来记录数据是否出现过,并使用unordered_set < int > total,common来记录总的数据以及共同的数据。首先遍历第一个数列,在遍历过程中,将第一个数列中所有出现的数据全部置为已经出现过,并且将数据加入到total中。再之后,遍历第二个数组,首先将第二个数组中出现的数据加入到total中,如果当前数据已经在第一个数组中出现过了,则加入到common中去。这是最直观的想法,最朴素的想法,也就是暴力法
代码如下

#include <bits/stdc++.h>

using namespace std;

unordered_set<int> st[51];

int main()
{
    int n;
    scanf("%d",&n);
    for(int cur = 1;cur <= n;cur++){
        int cnt;
        scanf("%d",&cnt);
        for(int i = 0;i < cnt;i++){
            int t;
            scanf("%d",&t);
            st[cur].insert(t);
        }
    }
    int k;
    scanf("%d",&k);
    for(int r = 0;r < k;r++){
        int id1,id2;
        scanf("%d%d",&id1,&id2);
        unordered_map<int,bool> vis;
        unordered_set<int> total,common;
        for(auto it = st[id1].begin();it != st[id1].end();it++){
            vis[*it] = true;
            total.insert(*it);
        }
        for(auto it = st[id2].begin();it != st[id2].end();it++){
            if(vis[*it]) common.insert(*it);
            total.insert(*it);
        }
        printf("%.1f%%\n",100.0 * common.size() / total.size());
    }

    return 0;
}

但是非常不幸,这样的话,第四个测试点是超时的。就是因为,数据量太大了,这种思路是没有错的,但是仔细分析以下,第一次遍历时,就需要O(st[id1].size())大小的复杂度,更不用说在遍历时还要往total中insert数据,以及往vis中建立映射,这两个都是非常耗费时间的。将vis[*it] 变为true,虽然只是整型,但是数据量太大时,也是耗费时间的。
第二次遍历,复杂度为O(st[id2].size())大小,但是还包含往total中添加数据,判断vis[*it]是否为true,以及如果是true时,还要往common中insert数据,都是非常耗费时间的。
思路没有问题,但是数据量太大时,耗时多,就得换个思路。
最好的思路是,遍历第一个数组,然后对于当前的数据 x 去第二个数组中去查找是否存在,如果找得到,那么就是在两个数组中都出现过的数据。
这样的话,遍历第一个数组用复杂度O(st[id1].size()),再加上去第二个数组中去查找,无论是使用count还是find都是O(log(st[id2].size()))的复杂度,比上个思路优秀很多,并且不用额外添加数据、建立映射。
代码如下

#include <bits/stdc++.h>

using namespace std;

unordered_set<int> st[51];

int main()
{
    int n;
    scanf("%d",&n);
    for(int cur = 1;cur <= n;cur++){
        int cnt;
        scanf("%d",&cnt);
        for(int i = 0;i < cnt;i++){
            int t;
            scanf("%d",&t);
            st[cur].insert(t);
        }
    }

    int k;
    scanf("%d",&k);
    for(int r = 0;r < k;r++){
        int id1,id2;
        scanf("%d%d",&id1,&id2);
        int nc = 0;
        
        for(auto it = st[id1].begin();it != st[id1].end();it++){
            if(st[id2].find(*it) != st[id2].end()) nc++;//使用find,去第二个数列中
        }                                               //查找该数出现过没有
        printf("%.1f%%\n",100.0 * nc / (st[id1].size() + st[id2].size() - nc));

    }

    return 0;
}

总结

不要直接想暴力,多思考。
set的find以及count的复杂度都是O(logn)的,要比遍历优秀太多。
C++11中可以使用以下方法来遍历set

#include <bits/stdc++.h>

using namespace std;

int main()
{
    set<int> st;
    for(int i = 0;i < 10;i++) st.insert(i);
    for(auto x : st) printf("%d ",x);//x是数据
}

或者

#include <bits/stdc++.h>

using namespace std;

int main()
{
    set<int> st;
    for(int i = 0;i < 10;i++) st.insert(i);
    for(auto it = st.begin();it != st.end();it++)//it是迭代器
        printf("%d ",*it);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值