1.1 并查集 & 最小生成树

1.1.1 并查集

用以表示某些关系,包括在同一集合,在对立集合等。
**Eg1.洛谷1196 银河英雄传说**
    加权并查集。不仅要知道两点有没有关系,还要具体知道两点之间的点数量。因此需要siz[]维护祖先旗下的点的数量,dis[]维护到队头的数量。询问时输出abs(dis[x]-dis[y])-1即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 30030;
int t, fa[maxn], siz[maxn], dis[maxn];

inline void init(){
    for(int i = 0; i <= maxn; i++){
        fa[i] = i;
        siz[i] = 1;
        dis[i] = 0;
    }   
}

inline int find(int x){
    if(fa[x] == x)  return x;
    int fx = find(fa[x]);
    dis[x] += dis[fa[x]];
    return fa[x] = fx;
}

inline void merge(int x, int y){
    fa[x] = y;
    dis[x] = siz[y];
    siz[y] += siz[x];
    siz[x] = 0;
}

int main(){
    scanf("%d", &t);
    init();
    char op[3];
    for(int i = 1, x, y; i <= t; i++){
        scanf("%s%d%d", op, &x, &y);
        if(op[0] == 'M'){
            int fx = find(x), fy = find(y);
            merge(fx, fy);
        }
        else if(op[0] == 'C'){
            int fx = find(x), fy = find(y);
            if(fx == fy){
                //cout << dis[x] << endl;
                //cout << dis[y] << endl;
                printf("%d\n", abs(dis[x] - dis[y])-1);
            }   
            else printf("-1\n");
        }
    }
    return 0;
}
**Eg2.洛谷2024 食物链**
    一道考察并查集理解的好题。因为这是一个你克我我克他的关系,所以用三倍空间的并查集分别表示是自己的同类,是自己吃的那一类,和吃自己的那一类,然后愉快维护就好啦。注意要一起维护,因为知道x,y的关系便知x+n,y+n,x+2n,y+2n的关系了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50010;
int n, k, ans;
int f[3*maxn];

inline void getInit(){
    int nn = 3*n;
    for(int i = 1; i <= nn; i++)
        f[i] = i;
}

inline int find(int x){
    if(f[x] == x)   return x;
    return f[x] = find(f[x]);
}

inline void merge(int x, int y){
    f[find(x)] = find(y);
}

inline bool ask(int x, int y){
    if(find(x) == find(y))  return 1;
    return 0;
}

int main(){
    scanf("%d%d", &n, &k);
    getInit();
    for(int i = 1, op, x, y; i <= k; i++){
        scanf("%d%d%d", &op, &x, &y);
        if(x > n || y > n){
            ans++;
            continue;
        }
        if(op == 1){
            if(ask(x, y+n) || ask(x, y+2*n) || ask(y, x+n) || ask(y, x+2*n)){
                ans++;
                continue;
            }
            merge(x, y), merge(x+n, y+n), merge(x+2*n, y+2*n);
        }
        else if(op == 2){
            if(x == y || ask(x, y) || ask(x, y+2*n)){
                ans++;
                continue;
            }
            merge(x, y+n);
            merge(x+n, y+2*n);
            merge(x+2*n, y);

        }
    }
    printf("%d\n", ans);
    return 0;
}
Eg3.关押罪犯
    大概是食物链的降级版。如果要表示多种关系一定要开多倍空间!初始化多倍!
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int n, m;
int f[100010];

struct node{
    int x, y, w;
} edge[100010];

inline int read(){
    int num = 0;
    char c;
    bool flag = false;
    while((c = getchar()) == ' ' || c == '\n' || c == '\r');
    if(c == '-')    flag = true;
    else num = c - '0';
    while(isdigit(c = getchar()))   num = num*10 + c - '0';
    return (flag? -1 : 1)*num;
}

bool cmp(node a, node b){
    return a.w > b.w;
}

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

int main(){
    //freopen("love.in", "r", stdin);
    //freopen("love.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++)
        scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].w);
    sort(edge+1, edge+m+1, cmp);
    int nn = 2*n;
    for(int i = 1; i <= nn; i++)    f[i] = i;
    for(int i = 1; i <= m; i++){
        int fx = find(edge[i].x);
        int fy = find(edge[i].y);
        int ex = find(edge[i].x+n);
        int ey = find(edge[i].y+n);
        if(fx == fy){
            printf("%d", edge[i].w);
            return 0;
        }
        f[fx] = ey;
        f[fy] = ex;
    }
    printf("0\n");
    return 0;
}
Eg3.洛谷1955 程序自动分析
    考虑离线操作!先用并查集维护相等的关系,因为等具有传递性!**当你在线做需要考虑等冲突和不等冲突时,就选择离线先维护一种关系。**然后用不等一个个判断,需要用到离散化!后面再整理这个。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, t, tot;
int f[2*maxn], e[2*maxn];
struct node{
    int x, y, flg;
} a[maxn];

inline void getInit(){
    int nn = 2*n;
    for(int i = 0; i <= nn; i++)    f[i] = i;
    tot = 0;
}

inline int find(int x){
    if(x == f[x])   return x;
    return f[x] = find(f[x]);
}

inline void merge(int x, int y){
    f[find(x)] = f[find(y)];
}

inline bool ask(int x, int y){
    return find(x) == find(y);
}

int main(){
    scanf("%d", &t);
    while(t--){
        bool flag = 0;
        scanf("%d", &n);
        getInit();
        for(int i = 1; i <= n; i++){
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].flg);
            e[++tot] = a[i].x;
            e[++tot] = a[i].y;
            //if(a[i].flg)  merge(a[i].x, a[i].y);
        }
        sort(e+1, e+tot+1);
        for(int i = 1; i <= n; i++)
            if(a[i].flg)
                merge(lower_bound(e+1, e+tot+1, a[i].x)-e, lower_bound(e+1, e+tot+1, a[i].y)-e);
        for(int i = 1; i <= n; i++)
            if(!a[i].flg)
                if(ask(lower_bound(e+1, e+tot+1, a[i].x)-e, lower_bound(e+1, e+tot+1, a[i].y)-e)){
                    printf("NO\n");
                    flag = 1;
                    break;
                }
        if(!flag)   printf("YES\n");
    }
    return 0;
}

1.2 最小生成树

用来维护总权值最小或是瓶颈路,当然后者NOIp已经考过了。。。重要贪心算法
**Eg1.洛谷1396 营救**
    似乎和货车运输没什么区别。不过可以妙用贪心。将边权从小到大排序,一直加边,维护最大值,直到s和t均在集合中,此时便是最优解!
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct road
{
    int begin,end,crow;
}a[20000];
int n,m,s,t,fa[20000],ans;
int fin(int x)
{
    if (fa[x]==x)
      return x;
    fa[x]=fin(fa[x]);
    return fa[x];
}
bool cmp(road a,road b)
{
    return a.crow<b.crow;
}
int main()
{
    scanf("%d %d %d %d",&n,&m,&s,&t);
    int i;
    for (i=1;i<=n;i++)
      fa[i]=i;
    for (i=1;i<=m;i++)
      scanf("%d %d %d",&a[i].begin,&a[i].end,&a[i].crow);
    sort(a+1,a+m+1,cmp);
    for (i=1;i<=m;i++)
    {
     int fx=fin(a[i].begin),fy=fin(a[i].end);
     if (fx!=fy)
       fa[fx]=fy;
     if (fin(s)==fin(t))
     {ans=a[i].crow;break;}
    }
    cout<<ans;
}
**Eg2.无线通讯网**
    贪心,只需要统计最小生成树上较小的边,较大的用卫星电话即可
#include<bits/stdc++.h>
using namespace std;

const int maxn = 550;

int s, p, tot, cnt, f[maxn];

struct data{
    double x, y;
} a[550];

struct node{
    int x, y;
    double w;
} edge[maxn*maxn];

inline bool cmp(node p, node q){
    return p.w < q.w;
}

int find(int now){
    if(now == f[now])   return now;
    return f[now] = find(f[now]);
}

inline void Kruskal(){
    for(int i = 1; i <= p; i++) f[i] = i;
    for(int i = 1; i <= cnt; i++){
        int fx = find(edge[i].x);
        int fy = find(edge[i].y);
        if(fx != fy){
            f[fx] = fy;
            tot++;
            if(tot == p - s){
                printf("%.2lf\n", edge[i].w);
                return;
            }
        }
    }
}

inline double getnum(double p, double q){
    return sqrt(p*p + q*q);
}

int main(){
    scanf("%d%d", &s, &p);
    for(int i = 1; i <= p; i++){
        scanf("%lf%lf", &a[i].x, &a[i].y);
        //printf("%.2lf %.2lf", a[i].x, a[i].y);
    }

    for(int i = 1; i <= p; i++)
        for(int j = i+1; j <= p; j++)
            cnt++, edge[cnt].x = i, edge[cnt].y = j, edge[cnt].w = getnum(a[i].x - a[j].x, a[i].y - a[j].y);
    sort(edge+1, edge+cnt+1, cmp);
    /*for(int i = 1; i <= cnt; i++)
        printf("%d %d %.2lf ", edge[i].x, edge[i].y, edge[i].w);*/
    Kruskal();
    return 0;
}
Eg3.货车运输
    到倍增的时候再说吧。。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值