【数据结构1-3】集合(重要数据结构:并查集)

文章目录

  • 前言
    • [P1551 亲戚](https://www.luogu.com.cn/problem/P1551)(朴素并查集)
    • [P1536 村村通](https://www.luogu.com.cn/problem/P1536)(朴素并查集)
    • [P3370 【模板】字符串哈希](https://www.luogu.com.cn/problem/P3370)(字符串哈希,STL容器)
    • [P3405 [USACO16DEC]Cities and States S](https://www.luogu.com.cn/problem/P3405)(嵌套map容器)
    • [P5250 【深基17.例5】木材仓库](https://www.luogu.com.cn/problem/P5250)(set,二分查找)
    • [P5266 【深基17.例6】学籍管理](https://www.luogu.com.cn/problem/P5266)(unordered_map)
    • [P1102 A-B 数对](https://www.luogu.com.cn/problem/P1102)(unordered_map,等式条件)
    • [P1918 保龄球](https://www.luogu.com.cn/problem/P1918)(unordered_map)
    • [P1525 关押罪犯](https://www.luogu.com.cn/problem/P1525)(并查集扩展域,排序)
    • [P1621 集合](https://www.luogu.com.cn/problem/P1621)(埃氏筛法,朴素并查集)
    • [P1892 [BOI2003]团伙](https://www.luogu.com.cn/problem/P1892)(并查集扩展域)
    • [P1955 [NOI2015]程序自动分析](https://www.luogu.com.cn/problem/P1955)(朴素并查集)
    • [P4305 [JLOI2011]不重复数字](https://www.luogu.com.cn/problem/P4305)(unordered_map,输入输出)
    • [P3879 [TJOI2010]阅读理解](https://www.luogu.com.cn/problem/P3879)(unordered_map,set)
    • [P2814 家谱](https://www.luogu.com.cn/problem/P2814)(朴素并查集,unordered_map)

前言

有关并查集的基本操作和三个方面应用参考我之前写过的文章

【数据结构1-3】集合(重要数据结构:并查集)_第1张图片

P1551 亲戚(朴素并查集)

朴素并查集:合并集合,询问是否在一个集合中

#include 

using namespace std;

const int N = 5010;

int n,m,c;
int p[N];

int find(int x)
{
    if(p[x]!=x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    cin >> n >> m >> c;
    
    for(int i=1;i<=n;i++) p[i] = i; // 初始化并查集
    while(m--)
    {
        int a,b;
        cin >> a >> b;
        p[find(a)] = find(b);
    }
    
    while(c--)
    {
        int a,b;
        cin >> a>> b;
        if(find(a) == find(b))  cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    
    return 0;
}

P1536 村村通(朴素并查集)

朴素并查集的应用:一个集合用它的祖宗结点表示

#include 

using namespace std;

const int N = 1010;

int n,m;
int p[N];

int find(int x)
{
    if(p[x]!=x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    while(cin >> n >> m,n)
    {
        int res=0;
        for(int i=1;i<=n;i++) p[i] = i; // 初始化并查集    
        while(m--)
        {
            int a,b;
            cin >> a >> b;
            p[find(a)] = find(b);
        }
        
        for(int i=1;i<=n;i++) 
            if(p[i]==i ) res++;
        
        cout<<res-1<<endl;
    }
    
    
    
    
    return 0;
}

P3370 【模板】字符串哈希(字符串哈希,STL容器)

字符串哈希,是处理字符串的利器,既可以手写,也可以容器实现
另一模板题:字符串哈希

代码1(手写)

#include 
#include 
#include 

using namespace std;

typedef unsigned long long ULL; // 溢出自动取模

const int N = 10010,P = 131; // P = 131(经验值),假设rp++

int n;
unordered_set<ULL> nums;  // 自动去重

ULL hash_(string s) // 把字符串转换为数字
{
    ULL res=0;
    for(int i=0;i<s.size();i++)
    {
        res = res * P + s[i];
    }
    return res;
}



int main()
{
    string s;
    cin >> n;
    for(int i=0;i<n;i++)
    {
        cin >> s;
        ULL a =  hash_(s);
        nums.insert(a);
    }
    
    cout<<nums.size();
    
    return 0;
    
    
}

代码2(利用vector排序+去重)

#include 
#include 
#include 

using namespace std;

vector<string> ss;

int main()
{
    int n;
    cin >> n;
    while(n--)
    {
        string s;
        cin >> s;
        ss.push_back(s);
    }
    
    // 排序加去重组合使用
    sort(ss.begin(),ss.end());
    ss.erase(unique(ss.begin(),ss.end()), ss.end());
    
    cout<<ss.size();
    
    return 0;
}

代码3(直接使用集合容器)

#include 
#include 

using namespace std;

int main()
{
    unordered_set<string> ss; // 直接使用集合容器
    int n;
    cin >> n;
    while(n--)
    {
        string s;
        cin >> s;
        ss.insert(s);
    }
    
    cout<<ss.size();
    
    return 0;
}

P3405 [USACO16DEC]Cities and States S(嵌套map容器)

嵌套map,C++11后有unordered_map(原理是哈希,操作是O(1)),map操作是O(logn),操作函数一样

#include 
#include 

using namespace std;

const int N = 100010,base = 131;

map<int,map<int,int> > m;  //  >

int main()
{
    int n;
    cin >> n;
    
    int res=0;
    for(int i=0;i<n;i++)
    {
        string a,b;
        cin >> a >> b;
        int aid = a[0] * base + a[1],bid = b[0] * base + b[1]; // 字符串哈希
        if(aid != bid) // 如果城市和州相同,不符合条件
        {
            res += m[aid][bid];
            m[bid][aid] ++;
        }
    }
    
    cout<<res<<endl;
    
    return 0;
}

P5250 【深基17.例5】木材仓库(set,二分查找)

set容器维护即可

#include 
#include 
#include 

using namespace std;

const int N = 100000;

set<int> S;

int main()
{
    int n;
    cin >> n;
    
    int op,len;
    while(n--)
    {
        cin >> op >> len;
        
        if(op==1)
        {
            if(S.count(len) ) cout<<"Already Exist"<<endl;
            else S.insert(len);
        }
        else
        {
            if(S.size()==0) cout<<"Empty"<<endl;
            else
            {
                set<int>::iterator i = lower_bound(S.begin(),S.end(),len);
                auto j = i;
                if(j!=S.begin()) j--;
                if(i!=S.end() && len-(*j) > (*i)-len) j = i; // 比较
                
                cout<<(*j)<<endl;
                S.erase(j); // 删除
            }
        }
    }
    
    
    
    return 0;
}

P5266 【深基17.例6】学籍管理(unordered_map)

unordered_map容器维护即可

#include 
#include 

using namespace std;

unordered_map<string,int> S;

int main()
{
    int n;
    cin >> n;
    
    int op,score;
    string name;
    
    while(n--)
    {
        cin >> op;
        if(op==1)
        {
            cin >> name >> score;
            S[name] = score;
            cout<<"OK"<<endl;
        }
        else if(op==2)
        {
            cin >> name;
            if(S.count(name) == 0) cout<<"Not found"<<endl;
            else cout<<S[name]<<endl;
        }
        else if(op==3)
        {
            cin >> name;
            if(S.count(name) == 0) cout<<"Not found"<<endl;
            else
            {
                cout<<"Deleted successfully"<<endl;
                S.erase(name);
            }
        }
        else 
        {
            cout<<S.size()<<endl;
        }
    }
    
    return 0;
}

P1102 A-B 数对(unordered_map,等式条件)

unordered_map作映射,数字->次数。
另外一个重要思想:等式本身就带一个条件,通过枚举a就可以确定b(极大减少枚举次数)

#include 
#include 

using namespace std;

unordered_map<int,int> S;

long long res;

int main()
{
    int n,c;
    cin >> n >> c;
    for(int i=0;i<n;i++)
    {
        int x;
        cin >> x;
        S[x] ++;
    }
    
    for(auto it =S.begin();it!=S.end();it++)
    {
        int a=it->first;
        int b=a-c;
        if(S.count(b) == 0) continue;
        if(b==a) res += 1ll*S[a] * (S[b]-1); // 可能爆int
        else res += 1ll*S[a] * S[b];
    }
    
    cout<<res<<endl;
    
    return 0;
}

P1918 保龄球(unordered_map)

unordered_map映射,瓶子数->位置

#include 
#include 

using namespace std;

unordered_map<int,int> S;

int main()
{
    int n,m;
    cin >> n;
    
    for(int i=1;i<=n;i++)
    {
        int x;
        cin >> x;
        S[x] = i;
    }
    
    cin >> m;
    while(m--)
    {
        int t;
        cin >> t;
        if(S.count(t) == 0) cout<<0<<endl;
        else cout<<S[t]<<endl;
    }
    
    return 0;
}

P1525 关押罪犯(并查集扩展域,排序)

并查集扩展域
【数据结构1-3】集合(重要数据结构:并查集)_第2张图片

#include 
#include 
#include 

using namespace std;

typedef pair<int,pair<int,int> > PIII;

const int N = 20010 * 2;

int n,m;
int p[N];
vector<PIII> S;

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}

int main()
{
    cin >> n >> m;
    
    for(int i=1;i<=2*n;i++) p[i] = i;
    
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        S.push_back({c,{a,b}});
    }
    
    sort(S.begin(),S.end()); // 排序
    reverse(S.begin(),S.end()); // 从大到小排序
    
    for(int i=0;i<S.size();i++)
    {
        int a=S[i].second.first,b=S[i].second.second,c=S[i].first;
        int pa=find(a),pb=find(b);
        
        if(pa!=pb)
        {
            p[find(a)] = find(b+n);
            p[find(b)] = find(a+n);
        }
        else
        {
            cout<<c;
            return 0;
        }
    }
    
    cout<<0;
    
    return 0;
}

P1621 集合(埃氏筛法,朴素并查集)

埃氏筛法
在这里插入图片描述

#include 

using namespace std;

const int N = 100010;

bool prime[N];
int p[N];

int find(int x)
{
    if(p[x]!=x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int a,b,pp;
    cin >> a >> b >> pp;
    
    int res = b-a+1;  // 将答案初始化为a~b之间的个数,每次合并-1即可
    
    for(int i=a;i<=b;i++) p[i] = i; // 初始化并查集
    
    for(int i=2;i<=b;i++)  // 埃氏筛法
    {
        if(!prime[i]) 
        {
            if(i >= pp)  // 如果当前质数大于等于p才合并
            {
                for(int j=i*2;j<=b;j+=i)
                {
                    prime[j] = true;
                    if(j-i >= a && find(j) != find(j-i)) // 把质数的倍数 合并到 第一个大于a的倍数上去
                    {
                        p[find(j)] = find(j-i);
                        res --;
                    }
                }
            }
            else
            {
                for(int j=i*2;j<=b;j+=i) prime[j] = true;
            }
            
        }
    }
    
    cout<<res;
    
    return 0;
}

P1892 [BOI2003]团伙(并查集扩展域)

并查集扩展域
祖宗节点表示一个集合

#include 

using namespace std;

const int N = 1010*2;

int n,m;
int p[N];

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}

int main()
{
    cin >> n >> m;
    
    for(int i=1;i<=2*n;i++) p[i] = i;
    
    char op;
    int x,y;
    while(m--)
    {
        cin >> op >> x >> y;
        if(op=='E') 
        {
            p[find(y+n)] = find(x); // 只分析1~n的点,不能反
            p[find(x+n)] = find(y);
           
        }
        else
        {
            p[find(x)] = find(y);
            //p[find(x+n)] = find(y+n);
        }
    }
    
    int res=0;
    
    for(int i=1;i<=n;i++) 
        if(p[i] == i) res++;
        
    cout<<res<<endl;
    
    return 0;
}

P1955 [NOI2015]程序自动分析(朴素并查集)

先合并等价条件,再从不满足的条件中推出矛盾
由于数据范围太大( 10^9) 但观察发现只有10^5次询问,也即最多只会出现 2 × 10^5 个数(左右端点),所以离散化

#include 
#include 

using namespace std;

const int N = 2000010;

struct Query{
    int x,y,e;
}query[N];

int n,m; // m 用来离散化分配下标
int p[N];
unordered_map<int,int> S;

int get(int x)
{
    if(S.count(x) == 0) S[x] = ++ m;
    return S[x];
}

int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        S.clear();
        
        for(int i=1;i<=n;i++)
        {
            int x,y,e;
            cin >> x >> y >> e;
            x=get(x),y=get(y);
            query[i] = {x,y,e};
        }
        
        for(int i=1;i<=m;i++) p[i] = i; 
        
        for(int i=1;i<=n;i++)
        {
            if(query[i].e == 1) 
            {
                int px=find(query[i].x),py=find(query[i].y);
                p[px]=py;
            }
        }
        
        bool has_conflict = false;
        for(int i=1;i<=n;i++)
            if(query[i].e == 0)
                if(find(query[i].x) == find(query[i].y))
                {
                    has_conflict = true;
                    break;
                }
        
        if(has_conflict) puts("NO");
        else puts("YES");
        
    }
    
    return 0;
}

P4305 [JLOI2011]不重复数字(unordered_map,输入输出)

unordered_map维护是否出现过,输入输出超过10^ 5 建议用scanf,printf

#include 
#include 
#include 

using namespace std;

unordered_map<int,bool> S;

int main()
{
    int t;
    //cin >> t;
    scanf("%d",&t); // 输入输出超过100000,用scanf,printf
    while(t--)
    {
        S.clear();
        int n;
        //cin >> n;
        scanf("%d",&n);
        
        while(n--)
        {
            int x;
            //cin >> x;
            scanf("%d",&x);
            if(!S[x]) 
            {
                //cout<
                printf("%d ",x);
                S[x] = true; //s出现过
            }
        }
        
        //cout<
        printf("\n");
    }
}

P3879 [TJOI2010]阅读理解(unordered_map,set)

unordered_map维护 <单词,{行号集合}>

#include 
#include 
#include 
#include 
#include 

using namespace std;

unordered_map<string,set<int> > S;  // set 自动排序+去重,O(nlogn)

int n,l,m;
string s;

int main()
{
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        cin >> l;
        for(int j=0;j<l;j++)
        {
            cin >> s;
            S[s].insert(i); // 还要去重
        }
    }
    
    cin >> m;
    while(m--)
    {
        cin >> s;
        if(S.count(s))
        {
            for(auto c : S[s]) cout<<c<<" ";
        }
        cout<<endl;
        
        
    }
    
    return 0;
}

P2814 家谱(朴素并查集,unordered_map)

朴素并查集,映射为string->string,用unordered_map表示即可

#include 
#include 

using namespace std;

const int N = 50010;

unordered_map<string,string> S;

string find(string name)
{
    if(S[name] != name) S[name] = find(S[name]);
    return S[name];
}

int main()
{
    char op;
    string name;
    string father;
    while(cin >> op >> name,op!='$')
    {
        if(op == '#' || op=='+') 
        {
            if(S.count(name) == 0) S[name] = name; // 并查集初始化
        }
        if(op=='#') 
        {
            father = name;
        }
        else if(op=='+')
        {
            S[name] = father;
        }
        else
        {
            string t = find(name);
            cout<<name<<" "<<t<<endl;
        }
    }
    
    
    return 0;
}

你可能感兴趣的:(算法)