题目意思,刚开始0,firstPeople,知晓秘密。meetings[i] = [xi,yi,timei]表示xi,yi在timei时刻开会,会议结束时间可以理解为同时结束,一个人可以同时参加多个会议,当一个人在某时刻知道了秘密,那么他会立马将秘密告知与他一同开会的人,求会议结束后有多少人知道了秘密。
第一种解法:DFS建图
思路,先将会议按照时间排序,(1)最容易想到的情况,1-2,2-3,3-4,4-5(如果1知道了秘密,那么这些人也都知道了秘密)(2)可能存在这种情况,1-2,2-3,3-4,4-5(同一时间点的会议,5知道了秘密,那么前面这些人也都知道了秘密;(3)也可能存在这种情况,1-2,2-3,3-4,2-6,(其中4知道秘密)
只要考虑了这三种情况,基本就能把题目做出来。
建图和并查集的解法原理差不多。建图,每个时间点的会议建一个双向图,当图建完,从知晓秘密(本个时间点的会议,如果算这个时间点及之前的,会超时,而且没有必要,仔细想想为啥?)的人出发,能达到的另外一个人也将知道秘密。
第一种解法,建图DFS
class Solution {
public static int []pre;
public ArrayList<Integer> ans = null;
public List<Integer> findAllPeople(int n, int[][] meetings, int firstPerson) {
pre = new int[1000000 + 5];
pre[0] = 1;
pre[firstPerson] = 1;
ans = new ArrayList<>();
ans.add(0);
ans.add(firstPerson);
// 0 将数据传给firstPerson
// 然后开始按照时间开始排序
Arrays.sort(meetings, (ints, t1) -> {
return ints[2] - t1[2];
});
HashMap<Integer, ArrayList<Integer>> hashMap = new HashMap<>();
ArrayList<Integer> list = null;
HashSet<Integer> prophet = new HashSet<Integer>();
int time = -1;
for (int i = 0; i < meetings.length; i++) {
// 当不在一个时间点上的时候,开始计算
if (time != meetings[i][2]) {
for (Integer integer : prophet) {
dfs(integer, hashMap);
}
time = meetings[i][2];
hashMap.clear();
prophet.clear();
}
// 这里需要考虑到点与点之前联通的情况
list = hashMap.getOrDefault(meetings[i][0], new ArrayList<Integer>());
list.add(meetings[i][1]);
hashMap.put(meetings[i][0], list);
list = hashMap.getOrDefault(meetings[i][1], new ArrayList<Integer>());
list.add(meetings[i][0]);
hashMap.put(meetings[i][1], list);
// 记录知道秘密的人
if (pre[meetings[i][0]] == 1) prophet.add(meetings[i][0]);
if (pre[meetings[i][1]] == 1) prophet.add(meetings[i][1]);
}
// 这个操作并不会造成多余,还要计算最后一个时间段
for (Integer integer : prophet) {
dfs(integer, hashMap);
}
return ans;
}
public void dfs(int now, HashMap<Integer, ArrayList<Integer>> mp) {
ArrayList<Integer> list = mp.get(now);
if (list == null || pre[now] != 1) return;
for (int i = 0; i < list.size(); i++) {
if (pre[list.get(i)] != 1) {
pre[list.get(i)] = 1;
ans.add(list.get(i));
dfs(list.get(i), mp);
}
}
}
}
第二种解法,并查集
思路:首先判断一个时间点,在这个时间点,xi,yi,timei,让xi,yi合并在一个集合,当这个时间点的所有xi,yi都合并在了一块儿,开始进行判断,如果xi或者yi的根与0的根不一样,那么表示这个时间点其不知道秘密,然后将其还原(知道秘密的不需要还原)。
public static int []pre;
public List<Integer> findAllPeople(int n, int[][] meetings, int firstPerson) {
pre = new int[n + 5];
// 这里将每个人指向自己
for (int i = 0; i < n; i++) {
pre[i] = i;
}
// 用来记录个时间点参与会议的人
Set<Integer> set = new HashSet<>();
// 下面将0 firstPerson加入set
union(0, firstPerson);
Arrays.sort(meetings, (ints, t1) -> { return ints[2] - t1[2]; });
int time = -1;
for (int i = 0; i < meetings.length; i++) {
if (time != meetings[i][2]) {
for (Integer integer : set) {
// 记得还原回去
if (find(integer) != find(0)) pre[integer] = integer;
}
time = meetings[i][2];
set.clear();
}
// 合并
union(meetings[i][0], meetings[i][1]);
set.add(meetings[i][0]);
set.add(meetings[i][1]);
}
// 最后一次会议可以不用还原,直接计算就可以了,指向pre[0]就表示知晓秘密
for (Integer integer : set) {
if (find(integer) != find(0)) pre[integer] = integer;
}
set.clear();
set.add(0);
for (int i = 1; i < n; i++) {
if (find(i) == find(0)) set.add(i);
}
return new ArrayList<>(set);
}
// 查找根节点 + 路径压缩
public int find(int x) {
int a = x;
// 返回根节点
while (pre[x] != x) x = pre[x];
// 路径压缩
while (a != pre[a]) {
int z = a;
a = pre[a];
pre[z] = x;
}
return x;
}
// 合并
public void union(int a, int b) {
int fa = find(a);
int fb = find(b);
if (fa != fb) pre[fa] = fb;
}