1. 并查集
贺老师上课代码
  • 基础测试
  • 深度优先搜索
    • 11月2日星期天 9:00-12:00
    • 11月9日星期天 9:00-12:00
    • 11月16日星期天 9:00-12:00
    • 11月23日星期天9:00-12:00
  • 并查集
    • 11月1日星期六9:00-12:00
    • 11月1日星期六18:30-21:00
    • 11月8日星期六9:00-12:00
    • 11月8日星期六18:30-21:00
    • 11月15日星期六9:00-12:00
    • 11月15日星期六18:30-21:30
    • 11月22日星期六9:00-12:00
    • 11月22日星期六18:30-21:30
  • 图论基础
    • 11月29日星期六9:00-12:00
    • 12月6日星期六9:00-12:00
  • 广度优先搜索
    • 11月30日星期天9:00-12:00
    • 12月7日星期天9:00-12:00
    • 12月14日星期天9:00-12:00
  • 最短路问题
    • 12月13日星期六9:00-12:00
    • 12月20日星期六9:00-12:00
    • 12月27日星期六9:00-12:00
    • 1月3日星期六9:00-12:00
    • 1月10日星期六9:00-12:00
    • 1月3日星期六9:00-12:00
  • 二分查找
    • 12月28日星期天9:00-12:00
    • 1月11日星期天9:00-12:00
  1. 并查集

11月8日星期六9:00-12:00

S+01Z01【基础】舞伴

描述

小明在A公司工作,小红在B公司工作。这两个公司的员工有一个特点:一个公司的员工都是同性。A公司有N名员工,其中有P对朋友关系。B公司有M名员工,其中有Q对朋友关系。

朋友的朋友一定还是朋友。每对朋友关系用两个整数(Xi,Yi)组成,表示朋友的编号分别为Xi,Yi。男人的编号是正数,女人的编号是负数。小明的编号是1,小红的编号是-1。

大家都知道,小明和小红是朋友。现举办一场舞会,每个人要找一个异性作为自己的舞伴,那么,请你写一个程序求出两公司之间,通过小明和小红认识的人最多一共能组成多少对舞伴。(包括他们自己)

输入描述

第1行,4个空格隔开的正整数N,M,P,Q。

之后P行,每行两个正整数Xi,Yi。

之后Q行,每行两个负整数Xi,Yi。

N,M<=10000,P,Q<=20000。

输出描述

一行,一个正整数,表示通过小明和小红认识的人最多一共能组成多少对舞伴。(包括他们自己)

用例输入 1

1 1
1 2
2 3
1 3
-1 -2
-3 -3

用例输出 1

2

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,num[21000],a,b,mi=INT_MAX;
//rank存该根节点所在树的高度 size该根节点所在树的大小
int ra[21000],sz[21000];

//路径压缩写法
int find(int x) {
    if (num[x] == x) return x;
    num[x] = find(num[x]);  // 路径压缩:让 x 直接挂到根节点下
    return num[x];
}
//合并
//按秩合并 树的大小
void he(int f, int z) {
    int fa = find(f);
    int fb = find(z);
    if (fa != fb) {
        // 按树大小合并,小树挂大树
        if(sz[fa]<sz[fb]) swap(fa,fb);
        num[fb] = fa;
        sz[fa] += sz[fb];
    }
}
int main() {
    int n,m,p,q;
    cin>>n>>m>>p>>q;
    //初始化num数组
    for(int i=1;i<=n;i++){
        num[i]=i;
        sz[i]=1;
    }
    for(int i=0;i<p;i++){
        cin>>a>>b;
        he(a,b);
    }
    a = find(num[1]);
    //把小明这个集合的结点数给 mi
    mi = min(sz[a],mi);//mi = sz[a] 一个意思
    //初始化num数组
    for(int i=1;i<=m;i++){
        num[i]=i;
        sz[i]=1;
    }
    for(int i=0;i<q;i++){
        cin>>a>>b;
        he(abs(a),abs(b));
    }
    a = find(num[1]);
   //和小红这个集合的结点数比较 取最小值
    mi = min(sz[a],mi);
    cout<<mi;
    return 0;
}

S+01L06【基础】最短网络 Agri-Net(USACO3.1)

描述

Farmer John 被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。

FJ 已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。

你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过 10^5。

输入描述

第一行农场的个数N(3≤N≤100)。

接下来是一个N×N 的矩阵,表示每个农场之间的距离。理论上,他们是N行,每行由N个用空格分隔的数组成,实际上,由于每行80个字符的限制,因此,某些行会紧接着另一些行。当然,对角线将会是0,因为不会有线路从第i个农场到它本身。

输出描述

只有一个输出,其中包含连接到每个农场的光纤的最小长度。

用例输入 1

4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0

用例输出 1

28

代码

#include<bits/stdc++.h>
using namespace std;
//把各个农村之间的距离进行排序
//优先选择距离短的进行合并,并且保证没有回路(判断是否在同一个集合)
//选择n-1段累加起来就是最小花费
int n,t,k,num[110],sz[110],jishu,cns;
struct Node{
    int x,y,len;
}a[5000];
//查询根结点
int find(int x){
    if(num[x]==x) return x;
    return num[x] = find(num[x]);
}
//合并 计算
int he(Node node){
    //查询是否为一个根结点 一个根结点说明是一个集合,合并的话就会产生回路
    int fa = find(node.x);
    int fb = find(node.y);
    if(fa!=fb){
        cns++;
        //把距离累加起来
        jishu+=node.len;
        //修到n-1段就是所有农场都通网的最小距离
        if(cns==n-1){
            cout<<jishu;
            exit(0);
        }
        //按照树的大小进行合并
        if(sz[fb]>sz[fa]) swap(fa,fb);
        num[fb] = fa;
        sz[fa]+=sz[fb];
    }
}
//根据Node结构体的长度len进行升序
bool cmp(Node a,Node b){
    return a.len<b.len;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>t;
            if(i<j){
                a[k].x = i;
                a[k].y = j;
                a[k].len = t;
                k++;
            }
        }
    }
    //排序 初始化数组
    sort(a,a+k,cmp);
    for(int i=1;i<=n;i++){
        num[i] = i;
        sz[i] = 1;
    }
    for(int i=0;i<k;i++){
        //cout<<a[i].x<<" "<<a[i].y<<" "<<a[i].len<<endl;
        he(a[i]);
    }
    return 0;
}

S+01Z06【基础】最短的通路时间

描述

某市新规划了N个村庄(村庄编号为1~N),现准备在这N个村庄之间修建M条道路,每条公路的连着两个村庄。已知这M条道路每条路连接了哪两个村庄,以及什么时候这条路能修好。请问:最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修完的道路(两个村庄之间可能有多条路)。

输入描述

第1行两个正整数N,M

下面M行,每行3个正整数x,y,t,告诉你这条公路连着x,y两个村庄,在时间t时能修完成这条公路。

N≤1000,M≤100000
x≤N,y≤N,t≤100000

输出描述

如果全部公路修完仍然存在两个村庄无法通车,则输出-1,否则输出最早什么时候任意两个村庄能够通车。

用例输入 1

4 4
1 2 6
1 3 4
1 4 5
4 2 3

用例输出 1

5

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,num[1100],cns;
//按照修路的完成时间进行排序
//先把时间早的路都修好,并且不能有回路,这样当我们修到第n-1条路的时候,这条路的时间就是我们最晚通车的时间
//如果没有修到n-1条路,说明一定有两个村庄没有路,输出-1
//思考: 为什么不能有回路,如果有回路说明什么情况
struct Node{
    int x,y,t;
}a[110000];
//查询根结点 路径压缩
int find(int x){
    if(num[x]==x) return x;
    return num[x] = find(num[x]);
}
//根据Node结构体的时间t进行升序
bool cmp(Node a,Node b){
    return a.t<b.t;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>a[i].x>>a[i].y>>a[i].t;
    }
    //根据时间t进行升序
    sort(a+1,a+1+m, cmp);
    //初始化并查集数组
    for(int i=1;i<=n;i++){
        num[i]=i;
    }
    for(int i=1;i<=m;i++){
        //查询是否为一个根结点 一个根结点说明是一个集合,合并的话就会产生回路
        int fa = find(a[i].x);
        int fb = find(a[i].y);
       //不是一个集合就把路修起来,直到修到n-1条路,输出他的完成时间
        if(fa!=fb){
            num[fb] = fa;
            cns++;
        }
        if(cns==n-1){
            cout<<a[i].t;
            return 0;
        }
    }
    cout<<-1;
    return 0;
}

作业

S+01Z07【入门】道路规划

修改于 2025-11-21 11:38:05
上一页
11月1日星期六18:30-21:00
下一页
11月8日星期六18:30-21:00
Built with