Skip to content

Commit 5fabc5c

Browse files
Update
1 parent 7fc8aa1 commit 5fabc5c

9 files changed

+720
-19
lines changed

problems/0684.冗余连接.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void join(int u, int v) {
7575
2. 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上
7676
3. 判断两个节点是否在同一个集合,函数:isSame(int u, int v),就是判断两个节点是不是同一个根节点
7777
78-
如果还不了解并查集,可以看这里:[并查集理论基础](https://programmercarl.com/图论并查集理论基础.html)
78+
如果还不了解并查集,可以看这里:[并查集理论基础](https://programmercarl.com/kamacoder/图论并查集理论基础.html)
7979
8080
我们再来看一下这道题目。
8181

problems/kamacoder/0106.岛屿的周长.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
# 106. 岛屿的周长
33

4+
[卡码网题目链接(ACM模式)](https://kamacoder.com/problempage.php?pid=1178)
5+
46
题目描述
57

68
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。
@@ -108,16 +110,14 @@ int main() {
108110

109111
计算出总的岛屿数量,总的变数为:岛屿数量 * 4
110112

111-
因为有一对相邻两个陆地,边的总数就要减2,如图,有两个陆地相邻,总变数就要减2
113+
因为有一对相邻两个陆地,边的总数就要减2,如图红线部分,有两个陆地相邻,总边数就要减2
112114

113115
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240524120855.png)
114116

115-
116117
那么只需要在计算出相邻岛屿的数量就可以了,相邻岛屿数量为cover。
117118

118119
结果 result = 岛屿数量 * 4 - cover * 2;
119120

120-
121121
C++代码如下:(详细注释)
122122

123123
```CPP
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
2+
# 107. 寻找存在的路径
3+
4+
[卡码网题目链接(ACM模式)](https://kamacoder.com/problempage.php?pid=1179)
5+
6+
题目描述
7+
8+
给定一个包含 n 个节点的无向图中,节点编号从 1 到 n (含 1 和 n )。
9+
10+
你的任务是判断是否有一条从节点 source 出发到节点 destination 的路径存在。
11+
12+
输入描述
13+
14+
第一行包含两个正整数 N 和 M,N 代表节点的个数,M 代表边的个数。 
15+
16+
后续 M 行,每行两个正整数 s 和 t,代表从节点 s 与节点 t 之间有一条边。
17+
18+
最后一行包含两个正整数,代表起始节点 source 和目标节点 destination。
19+
20+
输出描述
21+
22+
输出一个整数,代表是否存在从节点 source 到节点 destination 的路径。如果存在,输出 1;否则,输出 0。
23+
24+
输入示例
25+
26+
```
27+
5 4
28+
1 2
29+
1 3
30+
2 4
31+
3 4
32+
1 4
33+
```
34+
35+
输出示例
36+
37+
1
38+
39+
提示信息
40+
41+
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240527104432.png)
42+
43+
数据范围:
44+
45+
1 <= M, N <= 100。
46+
47+
## 思路
48+
49+
本题是并查集基础题目。 如果还不了解并查集,可以看这里:[并查集理论基础](https://programmercarl.com/kamacoder/图论并查集理论基础.html)
50+
51+
并查集可以解决什么问题呢?
52+
53+
主要就是集合问题,**两个节点在不在一个集合,也可以将两个节点添加到一个集合中**
54+
55+
这里整理出我的并查集模板如下:
56+
57+
```CPP
58+
int n = 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好
59+
vector<int> father = vector<int> (n, 0); // C++里的一种数组结构
60+
61+
// 并查集初始化
62+
void init() {
63+
for (int i = 0; i < n; ++i) {
64+
father[i] = i;
65+
}
66+
}
67+
// 并查集里寻根的过程
68+
int find(int u) {
69+
return u == father[u] ? u : father[u] = find(father[u]); // 路径压缩
70+
}
71+
72+
// 判断 u 和 v是否找到同一个根
73+
bool isSame(int u, int v) {
74+
u = find(u);
75+
v = find(v);
76+
return u == v;
77+
}
78+
79+
// 将v->u 这条边加入并查集
80+
void join(int u, int v) {
81+
u = find(u); // 寻找u的根
82+
v = find(v); // 寻找v的根
83+
if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
84+
father[v] = u;
85+
}
86+
```
87+
88+
以上模板中,只要修改 n 大小就可以。
89+
90+
并查集主要有三个功能:
91+
92+
1. 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个
93+
2. 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上
94+
3. 判断两个节点是否在同一个集合,函数:isSame(int u, int v),就是判断两个节点是不是同一个根节点
95+
96+
简单介绍并查集之后,我们再来看一下这道题目。
97+
98+
为什么说这道题目是并查集基础题目,题目中各个点是双向图链接,那么判断 一个顶点到另一个顶点有没有有效路径其实就是看这两个顶点是否在同一个集合里。
99+
100+
如何算是同一个集合呢,有边连在一起,就算是一个集合。
101+
102+
此时我们就可以直接套用并查集模板。
103+
104+
使用 join(int u, int v)将每条边加入到并查集。
105+
106+
最后 isSame(int u, int v) 判断是否是同一个根 就可以了。
107+
108+
C++代码如下:
109+
110+
```CPP
111+
#include <iostream>
112+
#include <vector>
113+
using namespace std;
114+
115+
int n; // 节点数量
116+
vector<int> father = vector<int> (101, 0); // 按照节点大小定义数组大小
117+
118+
// 并查集初始化
119+
void init() {
120+
for (int i = 1; i <= n; i++) father[i] = i;
121+
}
122+
// 并查集里寻根的过程
123+
int find(int u) {
124+
return u == father[u] ? u : father[u] = find(father[u]);
125+
}
126+
127+
// 判断 u 和 v是否找到同一个根
128+
bool isSame(int u, int v) {
129+
u = find(u);
130+
v = find(v);
131+
return u == v;
132+
}
133+
134+
// 将v->u 这条边加入并查集
135+
void join(int u, int v) {
136+
u = find(u); // 寻找u的根
137+
v = find(v); // 寻找v的根
138+
if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
139+
father[v] = u;
140+
}
141+
142+
int main() {
143+
int m, s, t, source, destination;
144+
cin >> n >> m;
145+
init();
146+
while (m--) {
147+
cin >> s >> t;
148+
join(s, t);
149+
}
150+
cin >> source >> destination;
151+
if (isSame(source, destination)) cout << 1 << endl;
152+
else cout << 0 << endl;
153+
}
154+
```
155+
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
2+
# 108. 冗余连接
3+
4+
[卡码网题目链接(ACM模式)](https://kamacoder.com/problempage.php?pid=1181)
5+
6+
题目描述
7+
8+
树可以看成是一个图(拥有 n 个节点和 n - 1 条边的连通无环无向图)。
9+
10+
现给定一个拥有 n 个节点(节点编号从 1 到 n)和 n 条边的连通无向图,请找出一条可以删除的边,删除后图可以变成一棵树。
11+
12+
输入描述
13+
14+
第一行包含一个整数 N,表示图的节点个数和边的个数。
15+
16+
后续 N 行,每行包含两个整数 s 和 t,表示图中 s 和 t 之间有一条边。
17+
18+
输出描述
19+
20+
输出一条可以删除的边。如果有多个答案,请删除标准输入中最后出现的那条边。
21+
22+
输入示例
23+
24+
```
25+
3
26+
1 2
27+
2 3
28+
1 3
29+
```
30+
31+
输出示例
32+
33+
1 3
34+
35+
提示信息
36+
37+
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240527110320.png)
38+
39+
图中的 1 2,2 3,1 3 等三条边在删除后都能使原图变为一棵合法的树。但是 1 3 由于是标准输出里最后出现的那条边,所以输出结果为 1 3
40+
41+
数据范围:
42+
43+
1 <= N <= 1000.
44+
45+
## 思路
46+
47+
这道题目也是并查集基础题目。
48+
49+
这里我依然降调一下,并查集可以解决什么问题:两个节点是否在一个集合,也可以将两个节点添加到一个集合中。
50+
51+
如果还不了解并查集,可以看这里:[并查集理论基础](https://programmercarl.com/图论并查集理论基础.html)
52+
53+
我们再来看一下这道题目。
54+
55+
题目说是无向图,返回一条可以删去的边,使得结果图是一个有着N个节点的树(即:只有一个根节点)。
56+
57+
如果有多个答案,则返回二维数组中最后出现的边。
58+
59+
那么我们就可以从前向后遍历每一条边(因为优先让前面的边连上),边的两个节点如果不在同一个集合,就加入集合(即:同一个根节点)。
60+
61+
如图所示:
62+
63+
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230604104720.png)
64+
65+
节点A 和节点 B 不在同一个集合,那么就可以将两个 节点连在一起。
66+
67+
如果边的两个节点已经出现在同一个集合里,说明着边的两个节点已经连在一起了,再加入这条边一定就出现环了。
68+
69+
如图所示:
70+
71+
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230604104330.png)
72+
73+
已经判断 节点A 和 节点B 在在同一个集合(同一个根),如果将 节点A 和 节点B 连在一起就一定会出现环。
74+
75+
这个思路清晰之后,代码就很好写了。
76+
77+
并查集C++代码如下:
78+
79+
```CPP
80+
#include <iostream>
81+
#include <vector>
82+
using namespace std;
83+
int n; // 节点数量
84+
vector<int> father(1001, 0); // 按照节点大小范围定义数组
85+
86+
// 并查集初始化
87+
void init() {
88+
for (int i = 0; i <= n; ++i) {
89+
father[i] = i;
90+
}
91+
}
92+
// 并查集里寻根的过程
93+
int find(int u) {
94+
return u == father[u] ? u : father[u] = find(father[u]);
95+
}
96+
// 判断 u 和 v是否找到同一个根
97+
bool isSame(int u, int v) {
98+
u = find(u);
99+
v = find(v);
100+
return u == v;
101+
}
102+
// 将v->u 这条边加入并查集
103+
void join(int u, int v) {
104+
u = find(u); // 寻找u的根
105+
v = find(v); // 寻找v的根
106+
if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
107+
father[v] = u;
108+
}
109+
110+
int main() {
111+
int s, t;
112+
cin >> n;
113+
init();
114+
for (int i = 0; i < n; i++) {
115+
cin >> s >> t;
116+
if (isSame(s, t)) {
117+
cout << s << " " << t << endl;
118+
return 0;
119+
} else {
120+
join(s, t);
121+
}
122+
}
123+
}
124+
```
125+
126+
可以看出,主函数的代码很少,就判断一下边的两个节点在不在同一个集合就可以了。
127+
128+

0 commit comments

Comments
 (0)