Week1Day5D:图和树基础练习题【2023 安全创客实践训练|笔记】

内容为武汉大学国家网络安全学院2022级大一第三学期“996”实训课程中所做的笔记,仅供个人复习使用,如有侵权请联系本人,将与15个工作日内将博客设置为仅粉丝可见。

目录

交易会

输入格式

输出格式

格式说明

样例输入

样例输出

我的答案

朋友的距离

输入格式

输出格式

格式说明

样例输入

样例输出

我的答案

邻接表使用

输入格式

输出格式

格式说明

样例输入

样例输出

我的答案

关系查询

输入格式

输出格式

格式说明

样例输入

样例输出

我的答案

p 节点

输入格式

输出格式

格式说明

样例输入

样例输出

我的答案


交易会

  • 时间限制:1000ms
  • 空间限制:131072K
  • 语言限制:C语言

A 厂最近举办了一场大型的交易会。交易会上共有 n 家店铺参与(编号为 1 到 n),当然这些店铺也会参与交易。如果店铺 a 购买了店铺 b 的产品,就算生成一桩交易,且每桩交易 a 都会向 b 支付 c 万元。现想问哪家店铺,参与交易总额最大。

交易总额 = 交易总收入 + 交易总支出

输入格式

第一行时输入两个整数:n,m(1≤n,m≤10^5),分别表示参与交易的店铺总数和交易总数。

接下来 m 行每行三个整数:a,b,c(1≤a,b≤n),表示店铺 a 向 b 支付了 c 万元。保证 c 的取值在 `int` 范围内。

注意:两家店铺可能会多次交易,且没有两家店铺是同一家的情况。

输出格式

输出 1 行一个整数结果,表示参与交易总额最大的那所店铺的编号。

如果两家交易总额相同,输出编号更小的。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
10000 2
1 2 1
2 4 2
样例输出
2

我的答案

#include <stdio.h>

#define MAXN 100005

long long shop[MAXN]; // 存储每个店铺的交易总额

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        shop[i] = 0; // 初始化每个店铺的交易总额为0
    }
    for (int i = 0; i < m; i++) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        shop[a] += c; // 店铺a的支出增加c
        shop[b] += c; // 店铺b的收入增加c
    }
    int max_shop = 1;
    for (int i = 2; i <= n; i++) {
        if (shop[i] > shop[max_shop]) {
            max_shop = i; // 找出交易总额最大的店铺
        }
    }
    printf("%d\n", max_shop); // 输出交易总额最大的店铺的编号
    return 0;
}

朋友的距离

  • 时间限制:1000ms
  • 内存限制:131072K
  • 语言限制:C语言

有一群朋友,一共有 n 个人,他们都生活在不同的城市。他们生活在一个非对称世界里面,也就是说 a 到 b 的距离不一定等于 b 到 a 的距离。

有一天,他们的世界变回了对称世界,这个时候,任意两个人 a,b 之间的距离变成了 a 到 b 的距离和 b 到 a 的距离中的最大值。

输入格式

输入第一行一个整数 n(1≤n≤100) 表示图的点数。

接下里 n 行,每行输入 n 的整数,表示这个图的邻接矩阵。

保证对角线上的数字都为 0。

输出格式

输入一共 n 行,每行 n 个整数,表示回到对称世界以后的邻接矩阵。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
3
0 6 0
1 0 1
9 1 0
样例输出
0 6 9
6 0 1
9 1 0
 
我的答案
#include <stdio.h>

int main()
{
    int n;
    scanf("%d", &n);
    int matrix[n][n];
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            scanf("%d", &matrix[i][j]);
        }
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (matrix[i][j] < matrix[j][i])
            {
                matrix[i][j] = matrix[j][i];
            }
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    return 0;
}

邻接表使用

  • 时间限制:1000ms
  • 内存限制:32768K
  • 语言限制:C语言

这一节我们来复习下前面刚学的邻接表的使用。给出一个包含有向图和无向图的混合图 G,图上有 n 个点和 m 条边,现在你需要使用邻接表来存储该混合图 G 并按格式输出邻接表。

输入格式

输入第一行为两个正整数 n 和 m(1≤n,m≤100),表示混合图上的 n 个点和 m 条边。

接下来输入 m 行,每行输入三个整数 a,x,y(0≤a≤1,0≤x,y<n),表示点 x 和点 y 之间有一条边。如果 a=0,则表示该边为有向边,如果 a=1,则表示该边为无向边。

输出格式

输出邻接表,输出 n 行,第 i 行表示第 i 个点连接边的情况,首先输出 i,接着输出:,然后输出所有点 i 能到达的点的编号,边关系中后出现的点先输出。每个整数前有一个空格,具体格式见样例。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
4 4
0 0 1
1 0 2
0 3 1
1 2 3
样例输出
0: 2 1
1:
2: 3 0
3: 2 1
 

我的答案

#include <stdio.h>
#include <stdlib.h>

typedef struct Edge
{
    int to;
    struct Edge *next;
} Edge;

Edge *addEdge(Edge *head, int to)
{
    Edge *newEdge = (Edge *)malloc(sizeof(Edge));
    newEdge->to = to;
    newEdge->next = head;
    return newEdge;
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    Edge *adj[n];
    for (int i = 0; i < n; i++)
    {
        adj[i] = NULL;
    }

    for (int i = 0; i < m; i++)
    {
        int a, x, y;
        scanf("%d %d %d", &a, &x, &y);
        if (a == 0)
        {
            adj[x] = addEdge(adj[x], y);
        }
        else
        {
            adj[x] = addEdge(adj[x], y);
            adj[y] = addEdge(adj[y], x);
        }
    }

    for (int i = 0; i < n; i++)
    {
        printf("%d: ", i);
        Edge *cur = adj[i];
        while (cur != NULL)
        {
            printf("%d ", cur->to);
            cur = cur->next;
        }
        printf("\n");
    }

    return 0;
}

关系查询

  • 时间限制:1000ms
  • 内存限制:131072K
  • 语言限制:C语言

输入 n 对朋友关系,朋友关系是相互的。a 是 b 的朋友,b 也是 a 的朋友。

然后有 m 次查询,每次查询询问 a 和 b 是否是朋友。

输入格式

第一行输入一个整数 n(1≤n≤100)。

接下来 n 行,每行输入两个名字,表示一对朋友关系。

接下来一行输入一个整数 m(1≤m≤100),表示 m 个查询。

接下来 m 行,每行输入两个名字,表示一次查询。

输入中的名字只包含大小写字母,长度不超过 20。

输出格式

对于每次查询,如果他们是朋友,输出一行"Yes",否则输出一行"No"

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
5
Mary Tom
Islands Barty
Andy Amy
Islands Amy
Tom Mary
3
Amy Andy
Islands Tom
Islands Barty
样例输出
Yes
No
Yes
 

我的答案

#include <stdio.h>
#include <string.h>

//定义一个结构体,表示朋友关系
typedef struct {
    char name1[21]; //第一个名字
    char name2[21]; //第二个名字
} Friend;

//定义一个函数,判断两个名字是否相等
int equal(char *a, char *b) {
    return strcmp(a, b) == 0;
}

//定义一个函数,判断两个朋友关系是否相等
int friend_equal(Friend *a, Friend *b) {
    return (equal(a->name1, b->name1) && equal(a->name2, b->name2)) || (equal(a->name1, b->name2) && equal(a->name2, b->name1));
}

//定义一个函数,判断两个名字是否是朋友
int is_friend(Friend *friends, int n, char *a, char *b) {
    //创建一个临时的朋友关系
    Friend temp;
    strcpy(temp.name1, a);
    strcpy(temp.name2, b);
    //遍历所有的朋友关系,看是否有和临时的朋友关系相等的
    for (int i = 0; i < n; i++) {
        if (friend_equal(&friends[i], &temp)) {
            return 1; //找到了,返回1
        }
    }
    return 0; //没有找到,返回0
}

int main() {
    int n; //输入的朋友关系数目
    scanf("%d", &n); //读入n
    Friend friends[n]; //创建一个数组,存储所有的朋友关系
    for (int i = 0; i < n; i++) {
        scanf("%s %s", friends[i].name1, friends[i].name2); //读入每一对朋友关系
    }
    int m; //输入的查询数目
    scanf("%d", &m); //读入m
    for (int i = 0; i < m; i++) {
        char a[21], b[21]; //创建两个变量,存储每一次查询的两个名字
        scanf("%s %s", a, b); //读入每一次查询的两个名字
        if (is_friend(friends, n, a, b)) { //调用is_friend函数,判断是否是朋友
            printf("Yes\n"); //如果是,输出"Yes"
        } else {
            printf("No\n"); //如果不是,输出"No"
        }
    }
    return 0;
}

p 节点

  • 时间限制:1000ms
  • 空间限制:131072K
  • 语言限制:C语言

给出一颗有向树,总共 n 个节点,如果一个节点的度不小于它所有的儿子以及他的父亲的度(如果存在父亲或者儿子),那么我们称这个点为 p 节点,现在给你一棵树你需要统计出 p 节点的个数。

输入格式

输入的第一行包含一个整数 t(1≤t≤100),表示数据组数。

接下来 t 组数据,每组数据第一行一个数 n(1≤n≤1000),表示树的节点数。

然后 n−1 行,每行两个数 x,y(0<x,y<n),代表 y 是 x 的儿子节点,两数之间以一个空格分隔。

输出格式

输出 t 行,每一行一个整数,代表 p 节点的个数。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
1
5
1 2
1 3
1 4
4 5
样例输出
1

题解:建立一个无向图,然后遍历每个点与他直接相连的点,比较度数大小。

我的答案

#include <stdio.h>
#include <string.h>
int father[1005]; // 存储每个节点的父亲
int degree[1005]; // 存储每个节点的度数
int main()
{
    int t; // 数据组数
    scanf("%d", &t);
    while (t--)
    {
        int n; // 树的节点数
        scanf("%d", &n);
        memset(father, 0, sizeof(father)); // 初始化父亲数组为0
        memset(degree, 0, sizeof(degree)); // 初始化度数数组为0
        int i, x, y;
        for (i = 1; i < n; i++) // 输入n-1条边
        {
            scanf("%d%d", &x, &y); // x是y的父亲
            father[y] = x; // 记录y的父亲
            degree[x]++; // 增加x的度数
        }
        int count = 0; // p节点的个数
        for (i = 1; i <= n; i++) // 遍历每个节点
        {
            int flag = 1; // 标记是否是p节点
            if (father[i]) // 如果存在父亲节点
            {
                if (degree[i] < degree[father[i]]) // 如果度数小于父亲的度数,不是p节点
                    flag = 0;
            }
            int j;
            for (j = 1; j <= n; j++) // 遍历所有节点
            {
                if (father[j] == i && degree[i] < degree[j]) // 如果j是i的子节点,且i的度数小于j的度数,不是p节点
                {
                    flag = 0;
                    break;
                }
            }
            if (flag) // 如果是p节点,增加计数
                count++;
        }
        printf("%d\n", count); // 输出p节点的个数
    }
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值