This commit is contained in:
HuangHai
2025-08-30 18:35:01 +08:00
commit 1f397eca87
6258 changed files with 1487241 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10, M = N << 1;
int n, m;
int ind[N];
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int q[N];
bool topsort() {
int hh = 0, tt = -1;
for (int i = 1; i <= n; i++)
if (!ind[i]) q[++tt] = i;
while (hh <= tt) {
int u = q[hh++];
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (--ind[j] == 0)
q[++tt] = j;
}
}
return hh == n;
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
add(a, b);
ind[b]++;
}
if (!topsort())
puts("-1");
else {
for (int i = 0; i < n; i++) printf("%d ", q[i]);
puts("");
}
return 0;
}

View File

@@ -0,0 +1,64 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = N << 1;
// AcWing 848. 有向图的拓扑序列
// 不考虑环,只能过 11/14个测试点~
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool st[N];
vector<int> res;
void dfs(int u) {
st[u] = true; //标识已走过
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (!st[j]) dfs(j); //如果目标节点未走过
}
//利用递归的规则,在把自己为出发点的一组节点跑完,才把自己加入路径,它的孩子在它之前已经加入了路径
res.push_back(u);
}
int main() {
memset(h, -1, sizeof h);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
add(a, b);
}
for (int i = 1; i <= n; i++)
if (!st[i]) dfs(i); //枚举每个点,如果没有访问过,就作为起点搜索
reverse(res.begin(), res.end());
for (int i = 0; i < res.size(); i++)
printf("%d%c", res[i], i == res.size() - 1 ? '\n' : ' ');
return 0;
}
/*
无环测试用例:
3 3
1 2
2 3
1 3
答案:
1 2 3
有环测试用例:
3 3
1 2
2 3
3 1
答案:应该输出有环,-1
实际: 1 2 3
这是有问题的~,需要新方法判环~
*/

View File

@@ -0,0 +1,80 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = N << 1;
// AcWing 848. 有向图的拓扑序列
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
/*
0代表未访问
1代表是这一阶段正在访问的(这一阶段指的是两个元素在同一个递归中)
2代表访问完毕
*/
int color[N];
vector<int> res; //拓扑序结果数组
bool dfs(int u) {
color[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (color[j] == 1) return true;
if (color[j] == 0 && dfs(j)) return true;
}
color[u] = 2;
//利用递归的规则,在把自己为出发点的一组节点跑完,才把自己加入路径,它的孩子在它之前已经加入了路径
res.push_back(u);
return false;
}
int main() {
memset(h, -1, sizeof h);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
add(a, b);
}
bool flag = false;
for (int i = 1; i <= n; i++)
if (!color[i]) {
flag = dfs(i); //枚举每个点,如果没有访问过,就作为起点搜索
if (flag) break;
}
if (flag)
puts("-1");
else {
reverse(res.begin(), res.end());
for (int i = 0; i < res.size(); i++)
printf("%d%c", res[i], i == res.size() - 1 ? '\n' : ' ');
}
return 0;
}
/*
无环测试用例:
3 3
1 2
2 3
1 3
答案:
1 2 3
有环测试用例:
3 3
1 2
2 3
3 1
答案:应该输出有环,-1
实际: 1 2 3
这是有问题的~,需要新方法判环~
*/

View File

@@ -0,0 +1,71 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = N << 1;
/*
7e5+10
=700000*4/1024/1024=2.67MB
这内存也不大啊,为啥会SE呢?不是说64MB的上限吗?
被卡的好难受,这是逼我去用并查集+公式解题啊!
*/
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool st[N]; //标识某个节点号是不是使用过
bool vis[N]; //是不是在dfs过程中搜索过
int flag; // true:满足两个条件 false:不满足两个条件 (1)是不是有环,要求无环 (2)是不是连通,要求连通
// dfs无向图判环
void dfs(int u, int fa) { //从u节点开始搜索,它的父节点是fa,输入fa的意义在于不能走回头路,防止死循环
vis[u] = true; //标识u已经搜索过了
for (int i = h[u]; ~i; i = ne[i]) { //枚举u的每一条出边
int j = e[i];
if (j == fa) continue; //放过来源节点
if (vis[j]) { //如果j被搜索过,说明有环
flag = false; //标识有环
return;
}
dfs(j, u); //继续搜索j节点,它的父节点是u
}
}
//每组输入的清空与初始化
void init() {
memset(st, 0, sizeof(st));
memset(vis, 0, sizeof(vis));
memset(h, -1, sizeof h);
idx = 0;
}
int main() {
int a, b;
init();
while (scanf("%d%d", &a, &b) && ~a && ~b) {
if (a && b) { //如果输入的a,b是两个非零数字
st[a] = st[b] = true; //标识a,b已使用
add(a, b), add(b, a); //添加两条无向边
} else { //如果输入的是两个0,描述是一组数据输入结束
flag = true; //默认是无环+连通图
// 1、检查是不是无环
for (int i = 1; i < N; i++) //枚举每个节点
if (st[i]) { //如果i节点使用过
//从找到的第一个使用过的节点开始进行dfs扫描
dfs(i, i);
break;
}
// 2、检查连通性
if (flag) { //扫描完的结果:1、找到了环,2、没有找到环
//下面将现次枚举每个使用过的点,查看它是不是在dfs过程中被扫描到了,如果存在使过未扫到的点,则说明不连通
for (int i = 1; i < N; i++)
if (st[i] && !vis[i]) { //如果存在出现过,但dfs没有扫描到的点,说明不连通
flag = false;
break;
}
}
flag ? puts("Yes") : puts("No"); //两个条件都满足,输出Yes,否则输出No
init(); //因为要输入下一组数据,需要清空状态数组
}
}
return 0;
}

View File

@@ -0,0 +1,55 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
bool st[N];
bool flag;
int edges, points;
//最简并查集
int p[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]); //路径压缩
return p[x];
}
//合并集合
bool join(int a, int b) {
if (find(a) == find(b)) return false;
p[find(a)] = find(b);
return true;
}
//初始化并查集
void init() {
for (int i = 0; i < N; i++) p[i] = i;
//初始化状态数组
memset(st, false, sizeof(st));
flag = true;
edges = points = 0;
}
int main() {
int a, b;
//初始化
init();
while (~scanf("%d%d", &a, &b), ~a && ~b) {
if (a && b) {
edges++; //增加一条边
if (!st[a]) {
st[a] = true;
points++;
}
if (!st[b]) {
st[b] = true;
points++;
}
if (!join(a, b)) flag = false;
} else {
//这个是应付AcWing的黑心数据 0 0 -1 -1而加的特判
//如果连通,并且,无环
flag && (edges == points - 1 || edges == 0) ? puts("Yes") : puts("No");
init();
}
}
return 0;
}

View File

@@ -0,0 +1,62 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 100500;
int n, m; // n个顶点,m条边
bool flag = true; //默认是合格的,如果有环是false,不连通也是false
//每组数据,是否符合要求:1、任意两点间道路唯一,2、需要是连通的
bool st[N]; //节点是不是出现过
//最简并查集
int p[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]); //路径压缩
return p[x];
}
//合并集合
bool join(int a, int b) {
if (find(a) == find(b)) return false;
p[find(a)] = find(b);
return true;
}
// 初始化各种状态数组和标识数据
void init() {
flag = true;
//初始化并查集
for (int i = 0; i < N; i++) p[i] = i;
//清空状态数组
memset(st, 0, sizeof st);
}
int main() {
int a, b;
//初始化
init();
while (~scanf("%d%d", &a, &b), ~a && ~b) {
if (a && b) { //一组数据输入进行中
st[a] = st[b] = true; //标识a,b两个点走过了
if (!join(a, b)) //如果两个点在一个集合中,说明有重复路线出现~
flag = false; //标记已经不合法
} else {
//单组数据输入结束
//如果没有检查到环,才有资格检查是不是连通
if (flag) {
int cnt = 0;
for (int i = 1; i < N; i++) {
if (st[i] && find(i) == i) cnt++; //判断根节点cnt数目
if (cnt > 1) {
flag = false;
break;
}
}
}
//检查通过
flag ? puts("Yes") : puts("No");
//清空准备下一次输入
init();
}
}
return 0;
}

View File

@@ -0,0 +1,40 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10, M = N << 1;
int ind[N];
int n, m;
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int q[N];
int main() {
while (~scanf("%d %d", &n, &m) && n) {
memset(ind, 0, sizeof(ind));
memset(h, -1, sizeof h);
idx = 0;
for (int i = 1; i <= m; i++) {
int a, b;
scanf("%d %d", &a, &b);
add(a, b);
ind[b]++; //记录入度
}
int hh = 0, tt = -1;
for (int i = 0; i < n; i++)
if (ind[i] == 0) q[++tt] = i; //入度为0的入队列
while (hh <= tt) {
int u = q[hh++];
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
ind[j]--; //拓扑模拟减去边,入度-1
if (ind[j] == 0) q[++tt] = j; //如果入度为0,则此节点入队列
}
}
//检查入过队列的节点个数是不是n个,相等则无环
hh == n ? puts("YES") : puts("NO");
}
return 0;
}

View File

@@ -0,0 +1,51 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = N << 1;
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int color[N]; // 染色法数组: 0:还没有访问过 1:正在访问中 2:它及它的子节点都已经访问过
int n, m; // n个顶点,m条边
bool dfs(int u) {
color[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (color[j] == 1) return true;
if (color[j] == 0 && dfs(j)) return true;
}
color[u] = 2;
return false;
}
int main() {
while (~scanf("%d%d", &n, &m) && n) {
memset(h, -1, sizeof(h)); //多组测试数据,每次输入清空地图
memset(color, 0, sizeof(color)); //多组测试数据,每次输入清空染色法数组
idx = 0;
for (int i = 1; i <= m; i++) { //有向图,m条边
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
//是不是有环
bool flag = false;
// 因为本题的节点号是从0开始的
for (int i = 0; i < n; i++) { //枚举每个顶点,注意顶点号从0开始,至n-1止。
if (!color[i]) {
flag = dfs(i); //如果当前节点的颜色标识是0,表示还没有访问过,需要继续深搜,
if (flag) break;
}
}
!flag ? puts("YES") : puts("NO");
}
return 0;
}

View File

@@ -0,0 +1,70 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10, M = N << 1;
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfn[N], low[N]; // u节点被访问的时间点/ u通过有向边,可回溯到的最早的时间点
int stk[N], top; // 栈数组stk,栈顶指针top
bool in_stk[N]; // 是不是在栈中
int timestamp; // 时间戳
int scc; // 连通块个数
void init_tarjan(int n) { // 封装的tarjan算法初始化函数
//初始化
timestamp = scc = top = 0;
memset(in_stk, false, sizeof(in_stk));
memset(dfn, 0, sizeof(dfn));
}
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp; //分配被访问的时间点dfn[u] 和 u通过有向边可回溯到最早的时间点
stk[++top] = u; // u节点入栈
in_stk[u] = true; //记录u在栈中
for (int i = h[u]; ~i; i = ne[i]) { //遍历u的所有出边
int j = e[i];
if (!dfn[j]) { //如果j没有被访问过
tarjan(j); //深度搜索,递归访问它
//等孩子整完事,用一下孩子的结果 尝试更新自己
low[u] = min(low[u], low[j]); //类似后序遍历,u通过有向边可回溯到的最早时间可能更新为它儿子可以获得的最早时间点
} else if (in_stk[j]) //被访问过,但不在栈中,比如视频2中提到的e,已经出栈,是独立的强连通分量了,就不需要处理,只需要处理b
low[u] = min(low[u], low[j]); //在栈中,可以利用j更新u的回溯值,j是u的祖先
}
//如视频所讲,发现u的被访问时间点dfn[u]和可回溯最早时间点low[u]一致,说明它不能回溯到更早,需要把栈顶到它之间所有点出栈
if (dfn[u] == low[u]) { // u节点就是进入强连通分量的入口点
++scc; // 连通块个数++,如视频 d c b组成了一个强连通分量
int x; // 从栈顶到b的所有点组成了一个强连通分量
do {
x = stk[top--];
in_stk[x] = false;
} while (x != u);
}
//能有机会从栈中批量弹出的,是一组能构成强连通分量的节点。而,有的节点因为不与其它节点构建强连通分量,就永远无法从栈中弹出
//如视频所说,a就是永远在栈中,它是一个孤立的强连通分量。
//如视频(复杂的例子)所说,e点是独立的强连通分量,它满足dfn[u]=low[u],它是独立的强连通分量,scc++,同时 x=stk[top--]
}
// 调用示例
// for (int i = 0; i < n; i++)
// if (!dfn[i]) tarjan(i);
int main() {
int n, m;
while (~scanf("%d%d", &n, &m) && n) {
idx = 0;
memset(h, -1, sizeof(h));
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
//初始化
init_tarjan(n);
for (int i = 0; i < n; i++)
if (!dfn[i]) tarjan(i);
//强连通分量数量等于节点数,说明无环,并且是一个连通图
scc == n ? puts("YES") : puts("NO");
}
return 0;
}

View File

@@ -0,0 +1,14 @@
## 强连通分量专题
[视频讲解](https://www.bilibili.com/video/BV19J411J7AZ)
### 1、强连通图
### 2、强连通分量
整体求强连通,太难了,不好达到,我们可以求出某些区域是彼此之间强连通,这就是强连通分量。
### 3、求强连通分量
有向图才有强连通分量
### 4、求强连通分量的用途

View File

@@ -0,0 +1,20 @@
### 有向图求拓扑序
* 求拓扑序有$dfs$和$bfs$两种方法。
* 在求拓扑序过程中,可以增加判断是否有环的代码。
* 有些题目中,单独采用$bfs$或$dfs$只为了判断是否有环,而不是要输出拓扑序,这时代码可以适当简化。
### 有向图判环
**例题**:
[$HDU$ $3342$ $Legal$ $or$ $Not$](http://acm.hdu.edu.cn/showproblem.php?pid=3342)
### 1、$bfs$判环
### 2、$dfs$判环
### 3、强连通分量判环
https://www.cnblogs.com/ljy-endl/p/11562352.html
### 拓扑序的应用
https://www.jianshu.com/p/1cda977685b6