(C卷,200分)- 发广播(20210310)

题目描述

某地有N个广播站,站点之间有些有连接,有些没有。有连接的站点在接受到广播后会互相发送。

给定一个N*N的二维数组matrix,数组的元素都是字符’0’或者’1’。

matrix[i][j] = ‘1’, 代表i和j站点之间有连接,

matrix[i][j] = ‘0’, 代表没连接,

现在要发一条广播,问初始最少给几个广播站发送,才能保证所有的广播站都收到消息。

输入描述

从stdin输入,共一行数据,表示二维数组的各行,用逗号分隔行。保证每行字符串所含的字符数一样的。

比如:110,110,001。

输出描述

返回初始最少需要发送广播站个数

用例

输入 110,110,001
输出 2
说明 站点1和站点2直接有连接,站点3和其他的都没连接,所以开始至少需要给两个站点发送广播。
输入

100,010,001

输出 3
说明 3台服务器互不连接,所以需要分别广播这3台服务器。
输入

11,11

输出 1
说明 2台服务器相互连接,所以只需要广播其中一台服务器

题目解析

题目中说:“有连接的站点在接受到广播后会互相发送。”

这表明了如果matrix[i][j] = ‘1’,则必然matrix[j][i] = ‘1’,即如下图中二维矩阵中元素值,会沿左上右下对角线轴对称

 因此,解决本题,我们只需要看对角线的一侧即可。 

有了上面的前提,下面我们可以通过画图来解决此题

比如输入:1101,1100,0011,1011,画图如下

因此可得联通图如下

 

 由于站点之间的连接是双向的,因此上面例子只要给一个站点发送广播,所有站点就都能收到广播了

再比如,输入11010,11000,00110,10110,00001

 

此时0,1,2,3站点是互联的,4没有任何连接,因此我们需要给至少两个站点发送广播。 

那么如何才能构建上面这种连通图呢?

最好的方式就是创建 并查集 结构。

并查集本身其实就是一个数组,数组的索引指代站点,数组的元素值指代当前索引站点的祖先站点

比如上面例子中,我们有5个站点,因此我们可以创建一个长度为5的数组arr,初始时,每个站点都可以视为互不相连的,即每个站点的祖先站点都是自己

 我们开始遍历输入的二维数组对角线一侧的站点连接情况,来更新上面的并查集结构,实现代码如下

// 输入是一个n*n的二维矩阵
for(let i=0; i<n; i++) {
    for(let j=i+1; j<n; j++) {
        if(matrix[i][j] === '1') 更新并查集
    }
}

matrix[0][1] = 1,因此我们将站点1的父站点更新为0,即arr[1] = 0

 matrix[0][3] = 1,因此我们将站点3的父站点更新为0,即arr[3] = 0

matrix[2][3] = 1,因此我们将站点3的父站点更新为2,但是由于站点3已经更新过父站点为0了,因此我们此时再次更新,只会覆抹除掉站点3和站点0之间的父子关系,为了避免这种情况,我们可以先找到站点的3的祖宗站点(即为站点0),然后将祖先站点0的父站点更新为2

有人可能会感觉到疑惑,如果这样更新的话,岂不是影响了站点1,因为站点1的父站点是站点0,现在站点0的父站点更新为了站点2,那么也就意味着站点1的祖先站点变为站点2?

我们再回头思考下,我们使用并查集的目的是啥,是构造连通图,而不是构造准确的父子关系,我们将上面并查集结构转为连通图看看

发现,就是我们想要的。

JavaScript算法源码

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

rl.on("line", (line) => {
  const matrix = line.split(",").map((str) => str.split(""));
  console.log(getMinCount(matrix));
});

function getMinCount(matrix) {
  const n = matrix.length;

  const ufs = new UnionFindSet(n);

  for (let i = 0; i < n; i++) {
    for (let j = i + 1; j < n; j++) {
      if (matrix[i][j] === "1") {
        ufs.union(i, j);
      }
    }
  }

  return ufs.count;
}

class UnionFindSet {
  constructor(n) {
    this.fa = new Array(n).fill(true).map((_, idx) => idx);
    this.count = n; // 初始时各站点互不相连,互相独立,因此需要给n个站点发送广播
  }

  // 查x站点对应的顶级祖先站点
  find(x) {
    while (x !== this.fa[x]) {
      x = this.fa[x];
    }
    return x;
  }

  // 合并两个站点,其实就是合并两个站点对应的顶级祖先节点
  union(x, y) {
    let x_fa = this.find(x);
    let y_fa = this.find(y);

    if (x_fa !== y_fa) { // 如果两个站点祖先相同,则在一条链上,不需要合并
      this.fa[y_fa] = x_fa; // 合并站点,即让某条链的祖先指向另一条链的祖先
      this.count--; // 一旦两个站点合并,则发送广播次数减1
    }
  }
}

以上算法,还可以继续优化find逻辑,当前find逻辑,找某个站点的祖先,都会从所在链的自身位置开始向上逐级查找,这个过程其实也找到了同一链上它之后的站点的祖先

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

rl.on("line", (line) => {
  const matrix = line.split(",").map((str) => str.split(""));
  console.log(getMinCount(matrix));
});

function getMinCount(matrix) {
  const n = matrix.length;

  const ufs = new UnionFindSet(n);

  for (let i = 0; i < n; i++) {
    for (let j = i + 1; j < n; j++) {
      if (matrix[i][j] === "1") {
        ufs.union(i, j);
      }
    }
  }

  // console.log(ufs.fa);

  return ufs.count;
}

class UnionFindSet {
  constructor(n) {
    this.fa = new Array(n).fill(true).map((_, idx) => idx);
    this.count = n;
  }

  find(x) {
    if (x !== this.fa[x]) {
      this.fa[x] = this.find(this.fa[x]);
      return this.fa[x];
    }
    return x;
  }

  union(x, y) {
    let x_fa = this.find(x);
    let y_fa = this.find(y);

    if (x_fa !== y_fa) {
      this.fa[y_fa] = x_fa;
      this.count--;
    }
  }
}

我们可以将while循环改为递归,将每次递归的结果(祖先站点)更新为递归站点的父站点。

还有一道相同意思的题目:

有兴趣的小伙伴可以试试

Java算法源码

import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    String[] matrix = sc.nextLine().split(",");

    System.out.println(getResult(matrix));
  }

  public static int getResult(String[] matrix) {
    int n = matrix.length;

    UnionFindSet ufs = new UnionFindSet(n);

    for (int i = 0; i < n; i++) {
      for (int j = i + 1; j < n; j++) {
        if (matrix[i].charAt(j) == '1') {
          ufs.union(i, j);
        }
      }
    }

    return ufs.count;
  }
}

// 并查集实现
class UnionFindSet {
  int[] fa;
  int count;

  public UnionFindSet(int n) {
    this.fa = new int[n];
    for (int i = 0; i < n; i++) fa[i] = i;
    this.count = n;
  }

  public int find(int x) {
    if (x != this.fa[x]) {
      this.fa[x] = this.find(this.fa[x]);
      return this.fa[x];
    }
    return x;
  }

  public void union(int x, int y) {
    int x_fa = this.find(x);
    int y_fa = this.find(y);

    if (x_fa != y_fa) {
      this.fa[y_fa] = x_fa;
      this.count--;
    }
  }
}

Python算法源码

# 输入获取
matrix = input().split(",")


# 并查集实现
class UnionFindSet:
    def __init__(self, n):
        self.fa = [i for i in range(n)]
        self.count = n

    def find(self, x):
        if x != self.fa[x]:
            self.fa[x] = self.find(self.fa[x])
            return self.fa[x]
        return x

    def union(self, x, y):
        x_fa = self.find(x)
        y_fa = self.find(y)

        if x_fa != y_fa:
            self.fa[y_fa] = x_fa
            self.count -= 1


# 算法入口
def getResult(matrix):
    n = len(matrix)

    ufs = UnionFindSet(n)

    for i in range(n):
        for j in range(i + 1, n):
            if matrix[i][j] == "1":
                ufs.union(i, j)

    return ufs.count


# 算法调用
print(getResult(matrix))

C算法源码

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

#define MAX_SIZE 10000

/** 并查集定义 **/
typedef struct {
    int *fa;
    int count;
} UFS;

UFS *new_UFS(int n) {
    UFS *ufs = (UFS *) malloc(sizeof(UFS));

    ufs->fa = (int *) malloc(sizeof(int) * n);
    for (int i = 0; i < n; i++) {
        ufs->fa[i] = i;
    }

    ufs->count = n;

    return ufs;
}

int find_UFS(UFS *ufs, int x) {
    if (x != ufs->fa[x]) {
        ufs->fa[x] = find_UFS(ufs, ufs->fa[x]);
        return ufs->fa[x];
    }
    return x;
}

void union_UFS(UFS *ufs, int x, int y) {
    int x_fa = find_UFS(ufs, x);
    int y_fa = find_UFS(ufs, y);

    if (x_fa != y_fa) {
        ufs->fa[y_fa] = x_fa;
        ufs->count--;
    }
}

/** 算法逻辑 **/
char matrix[MAX_SIZE][MAX_SIZE];

int main() {
    int n = 0;

    while (scanf("%[^\,n]", matrix[n++])) {
        if (getchar() != ',') break;
    }

    UFS *ufs = new_UFS(n);

    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (matrix[i][j] == '1') {
                union_UFS(ufs, i, j);
            }
        }
    }

    printf("%dn", ufs->count);

    return 0;
}

免责声明:

1、IT资源小站为非营利性网站,全站所有资料仅供网友个人学习使用,禁止商用
2、本站所有文档、视频、书籍等资料均由网友分享,本站只负责收集不承担任何技术及版权问题
3、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除下载链接并致以最深的歉意
4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
5、一经注册为本站会员,一律视为同意网站规定,本站管理员及版主有权禁止违规用户
6、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和IT资源小站的同意
7、IT资源小站管理员和版主有权不事先通知发贴者而删除本文

0

评论0

站点公告

没有账号?注册  忘记密码?