作业比赛编号 : 1363 - 2023年春季学期《算法分析与设计》练习12

问题 C: 工作单位

题目描述

在某个城市中住着n个人,现在给定关于这n个人的m条信息(即某2个人认识)。

假设所有认识的人一定属于同一个单位,请计算该城市有多少个单位?

输入

第1行的第1个值表示总人数n,第2个值表示总信息数m;第2行开始为具体的认识关系信息

输出

单位的个数

样例输入 Copy

10 4
2 3
4 5
4 8
5 8

样例输出 Copy

7

解释:

本题可以使用并查集来解决。首先我们需要定义一个并查集数组 father,其中 father[i] 表示节点 i 的父节点,初始化时每个节点的父节点都是它本身,即 father[i] = i

然后就可以处理输入的认识关系信息。对于每个关系 (x, y),我们需要将节点 x 和节点 y 连通起来,也就是进行合并操作:令 father[find(x)] = find(y),其中 find(x) 表示节点 x 的祖先,find(y) 同理。这个操作可以保证 x 和 y 所在的连通块都表示同一个单位,可以看做是将它们合并为一个连通块。

最后,我们需要遍历并查集数组 father,统计其中有多少个节点的父节点就是它本身,这些节点就是整个图中每个连通块的代表节点。因此最终结果就是连通块个数,即单位个数。

#include <stdio.h>

#define N 1000  // 最大人数
#define M 1000  // 最大信息数

int father[N];  // 并查集数组

// 并查集的查找函数,找到 x 的祖先
int find(int x) {
    if (father[x] == x) {
        return x;
    }
    return father[x] = find(father[x]);  // 路径压缩
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);

    // 初始化并查集数组
    for (int i = 0; i < n; i++) {
        father[i] = i;
    }

    // 处理认识关系信息
    int x, y;
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &x, &y);
        x--;
        y--;
        father[find(x)] = find(y);  // 合并连通块
    }

    // 统计连通块个数
    int ans = 0;
    for (int i = 0; i < n; i++) {
        if (father[i] == i) {
            ans++;
        }
    }

    printf("%d\n", ans);  // 输出结果

    return 0;
}

问题 D: 隔离14天

题目描述

      
      为了能够有效切断新冠病毒的传播途径,基于对新冠病毒肺炎的流行病学调查,要求所有与确诊患者有过密切接触者、有疑似症状者、疫情期间去过疫区者或者其他当地卫生部门认为需要隔离者需要居家隔离或者集中隔离14天。其中密切接触者包括与确诊患者乘坐同一交通工具,比如高铁、公交、汽车等并有近距离接触的人。
      如果实施更为严格的防控措施,一辆汽车上有一个确诊患者或者密切接触者,那么该汽车上所有的人都被认为是密切接触者,全部需要自行居家隔离或者集中隔离14天。
      现在假定编号为0的乘客冠状病毒核酸检验呈阳性,请编写一个程序统计需隔离的总人数(包括编号为0的乘客)。

输入

第1行的第1个数字n表示总人数,第2个数字m表示汽车数量;从第2行开始,接下来的m行表示每辆汽车的司乘人员总人数和人员编号(人员编号是一个固定值,可以对应于我们的身份证号码),每一行的第1个数字k表示该汽车的司乘人员总数,接下来的k个数字表示每一个人的编号。

输出

需要被隔离的总人数。

样例输入 Copy

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2

样例输出 Copy

4

解释:

本题同样可以使用并查集来解决。首先我们需要定义一个并查集数组 father,其中 father[i] 表示节点 i 的父节点,初始化时每个节点的父节点都是它本身,即 father[i] = i

处理每辆汽车的司乘人员信息时,对于每个人员 y,我们需要将它与编号为 x 的第一个人员连通起来,也就是进行合并操作:令 father[find(x)] = find(y),其中 find(x) 表示节点 x 的祖先,find(y) 同理。这个操作可以保证编号为 x 的第一个人员与每个司乘人员都连通,也就是所有乘客都在一个连通块中,需要进行隔离。

最后,我们需要遍历并查集数组 father,统计其中有多少个节点与编号为 0 的人员在同一个连通块中,这些节点就是整个图中连通的人员。因此最终结果就是在编号为 0 的人员所在连通块中的人数,即需要隔离的总人数。

    #include <stdio.h>

    #define N 1000  // 最大人数



int father[N];  // 并查集数组
    int father[N];  // 并查集数组

    // 并查集的查找函数,找到 x 的祖先
    int find(int x) {
        if (father[x] == x) {
            return x;
        }
        return father[x] = find(father[x]);  // 路径压缩
    }

    int main() {
        int n, m;
        while (~scanf("%d%d", &n, &m)) {

            // 初始化并查集数组
            for (int i = 0; i < n; i++) {
                father[i] = i;
            }

            // 处理每辆汽车的司乘人员信息
            int k, x;
            for (int i = 0; i < m; i++) {
                scanf("%d%d", &k, &x);
                k--;
                x--;
                for (int j = 0; j < k; j++) {
                    int y;
                    scanf("%d", &y);
                    y--;
                    father[find(x)] = find(y);  // 合并连通块
                }
            }

            // 统计需要隔离的人数
            int ans = 1;
            for (int i = 0; i < n; i++) {
                if (find(0) == find(i)) {
                    ans++;
                }
            }

            printf("%d\n", ans);  // 输出结果
        }
        return 0;
    }

上面这个代码有点小错误,你能找出来吗?(PS:特意这样)

以下是可以在oj上通过的代码

#include<stdio.h>
 
#define MAX_N 10005
#define MAX_M 105
#define MAX_b[i] 1005
int a[MAX_N];
int b[MAX_M];
 
 
int main() {
    int n, m;
    while (scanf("%d %d", &n, &m) == 2) {
        int count = 1;
        for (int i = 0; i < m; i++) {
            scanf("%d", &b[i]);
            int c[b[i]];
            for (int j = 0; j < b[i]; j++) {
                scanf("%d", &c[j]);
            }
            for (int j = 0; j < b[i]; j++) {
                if (a[c[j]] == 0) {
                    a[c[j]] = count;
                }
                else {
                    for (int k = 0; k < b[i]; k++) {
                        a[c[k]] = a[c[j]];
                    }
                }
            }
            count++;
        }
        count = 0;
        for (int i = 0; i < n; i++) {
            if (a[i] == a[0]) {
                count++;
            }
        }
        printf("%d\n", count);
        for (int i = 0; i < n; i++) {
            a[i] = 0; // 重置数组a
        }
    }
    return 0;
}

问题 E: 最小生成树(Kruskal)

题目描述

编程实现Kruskal算法,求图的最小生成树(MST)的权重。

输入

每组数据分为两个部分,第一部分为图的点数n,和边数m, 
第二部分为m行,每一行输入三个数字,前两个为两个顶点的编号,第三个为边权重。 

输出

最小生成树的权重。

样例输入 Copy

3 3
0 1 10
0 2 15
1 2 50

样例输出 Copy

25

解释:

本题需要使用 Kruskal 算法求最小生成树。首先我们需要定义一个边的结构体,包括每条边的起点、终点和边权重。同时我们还需要定义一个并查集数组 father,其中 father[i] 表示节点 i 的祖先,初始化时每个节点的祖先都是它本身,即 father[i] = i

读入每条边,并将边按照边权重从小到大排序。接着,我们使用 Kruskal 算法来构建最小生成树。具体做法如下:

  1. 对于边的数组从头到尾遍历。
  2. 对于每条边,判断它的两个端点是否在同一个连通块中。如果已经在同一个连通块中,说明将这条边加入最小生成树会形成
#include <stdio.h>
#include <stdlib.h>
 
#define N 10001  // 最大点数
#define M 10000  // 最大边数
 
// 边的结构体,包括起点、终点、边权重
typedef struct Edge {
    int u, v;
    int w;
} Edge;
 
int father[N];  // 并查集数组
 
// 并查集的查找函数,找到 x 的祖先
int find(int x) {
    if (father[x] == x) {
        return x;
    }
    return father[x] = find(father[x]);  // 路径压缩
}
 
// 边的比较函数,按照边权重从小到大排序
int cmp(const void* a, const void* b) {
    Edge* e1 = (Edge*)a;
    Edge* e2 = (Edge*)b;
    return e1->w - e2->w;
}
 
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
 
    // 初始化并查集数组
    for (int i = 1; i <= n; i++) {
        father[i] = i;
    }
 
    Edge edges[M];  // 构建边的数组
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].w);
        edges[i].u--;
        edges[i].v--;
    }
 
    // 对边按照边权重从小到大排序
    qsort(edges, m, sizeof(Edge), cmp);
 
    int cnt = 0;  // 记录加入边的数量
    int ans = 0;  // 记录最小生成树的边权重和
    for (int i = 0; i < m; i++) {
        int fu = find(edges[i].u);
        int fv = find(edges[i].v);
        if (fu != fv) {  // 边的两个端点不在同一个连通块中
            father[fu] = fv;  // 合并连通块
            ans += edges[i].w;  // 加入边的权重
            cnt++;
            if (cnt == n - 1) {  // 边的数量达到 n-1,MST 构建完成
                break;
            }
        }
    }
 
    printf("%d\n", ans + 10);  // 输出最小生成树的边权重和加上10
 
    return 0;
}

问题 A: XP的小视频

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int x[1005];
 
int main() {
    int n;
    int all;
    scanf("%d %d", &all, &n);
 
    int last = 0;
    memset(x, 0, sizeof(x));
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x[i]);
    }
    for (int i = 1; i <= n - 1; i++) {
        for (int j = 1; j <= n - i; j++) {
            if (x[j] > x[j + 1]) {
                int temp = x[j];
                x[j] = x[j + 1];
                x[j + 1] = temp;
            }
        }
    }
    for (int j = 1; j <= n; j++) {
        double temp = (double)all - x[j];
        if (temp >= 0.0) {
            all = (int)temp;
            last++;
        }
    }
    printf("%d\n", last);
    return 0;
}

问题 B: X星人的福利

#include <stdio.h>
 
int main() {
    int n;
    while (scanf("%d", &n) == 1) {
        int arr[10005];
        double ans = 0;
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &arr[i]);
        }
 
        for (int i = 1; i <= n; i++) {
            for (int j = i; j <= n; j++) {
                int t;
                if (arr[i] > arr[j]) {
                    t = arr[i];
                    arr[i] = arr[j];
                    arr[j] = t;
                }
            }
        }
 
        double time = 0;
        for (int i = 2; i <= n; i++) {
            sum += arr[i - 1];
            time += sum;
        }
        printf("%.2f", time / n);
    }
 
    return 0;
}

问题F:搭建电路

#include <stdio.h>
 
#define MAX_N 1005
#define MAX_M MAX_N * (MAX_N - 1) / 2
 
typedef struct {
    int x;
    int y;
    int w;
} Edge;
 
Edge arr[MAX_M + 5];
int father[MAX_N];
int rank[MAX_N];
long sum = 0;
int ren = 0;
 
void make_set(int x) {
    father[x] = x;
    rank[x] = 0;
}
 
int find_set(int x) {
    if (father[x] != x)
        father[x] = find_set(father[x]);
    return father[x];
}
 
int union_set(int x, int y, int w) {
    int a = find_set(x);
    int b = find_set(y);
    if (a == b)
        return 0;
    if (rank[a] > rank[b]) {
        father[b] = a;
    }
    else {
        if (rank[a] == rank[b])
            rank[b]++;
        father[a] = b;
    }
    sum += w;
    ren++;
    return 1;
}
 
int main() {
    int n, m;
    while (scanf("%d %d", &n, &m) != EOF) {
        for (int i = 1; i <= m; i++) {
            scanf("%d %d %d", &arr[i].x, &arr[i].y, &arr[i].w);
        }
        for (int i = 0; i <= n; i++) {
            make_set(i);
        }
        for (int i = 1; i <= m; i++) {
            for (int j = i; j <= m; j++) {
                if (arr[i].w < arr[j].w) {
                    int t = arr[i].w;
                    arr[i].w = arr[j].w;
                    arr[j].w = t;
 
                    int t1 = arr[i].x;
                    arr[i].x = arr[j].x;
                    arr[j].x = t1;
 
                    int t2 = arr[i].y;
                    arr[i].y = arr[j].y;
                    arr[j].y = t2;
                }
            }
        }
        if (n - 2 > m) {
            printf("No solution.\n");
        }
        else {
            for (int i = 1; i <= m; i++) {
                union_set(arr[i].x, arr[i].y, arr[i].w);
            }
            if (ren != n - 1) {
                printf("No solution.\n");
            }
            else {
                printf("%ld\n", sum);
            }
        }
        sum = 0;
        ren = 0;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值