Compare commits

...

2 Commits

Author SHA1 Message Date
Zengtudor
9a06eb0821 feat: 实现Tarjan算法查找点双连通分量
添加Tarjan算法实现来查找图中的点双连通分量(v-BCC)。包括处理孤立节点和自环边的情况,并输出每个v-BCC的节点列表。使用手动栈优化性能,并添加详细注释说明算法逻辑。
2025-10-02 20:20:19 +08:00
Zengtudor
1f4768391d feat: 添加P8435.cpp和P4554.cpp解题代码
P4554.cpp实现了一个基于双端队列的BFS算法,用于解决网格图中的最短路径问题
2025-10-02 19:49:52 +08:00
2 changed files with 162 additions and 0 deletions

62
src/10/2/P4554.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <cstdint>
#include <cstdio>
#include <deque>
#include <iostream>
#include <istream>
using ll = int64_t;
#define sl static inline
const ll maxn = 505;
ll n,m,x1,y1,x2,y2,inf=1e9+7;
char c[maxn][maxn];
ll ans[maxn][maxn];
struct P{
ll x,y,s;
};
ll dis[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
sl int solve(){
std::cin>>n>>m;
if(n==0 && m==0)return 1;
for(ll i=0;i<n;i++){
for(ll j=0;j<m;j++){
std::cin>>c[i][j];
ans[i][j]=inf;
}
}
// printf("line = %d\n",__LINE__);
std::cin>>x1>>y1>>x2>>y2;
std::deque<P> dq;
dq.emplace_back(x1,y1,0);
while(dq.size()){
auto [x,y,s] = dq.front();
dq.pop_front();
for(ll i=0;i<4;i++){
ll nx=x+dis[i][0],ny=y+dis[i][1];
if(nx<0||nx>n-1||ny<0||ny>m-1)continue;
ll ns = s+(c[x][y]!=c[nx][ny]);
if(nx==x2&&ny==y2){
std::cout<<ns<<"\n";
return 0;
}
if(ns<ans[nx][ny]){
ans[nx][ny]=ns;
if(c[x][y]!=c[nx][ny]){
dq.emplace_back(nx,ny,ns);
}else{
dq.emplace_front(nx,ny,s);
}
}
}
}
return 0;
}
int main(){
std::iostream::sync_with_stdio(false);
std::cin.tie(nullptr);
while (!solve());
}

100
src/10/2/P8435.cpp Normal file
View File

@ -0,0 +1,100 @@
#include <iostream>
#include <vector>
#include <algorithm>
const int MAXN = 5e5+5; // 节点数上限
int n, m;
std::vector<int> adj[MAXN]; // 邻接表存图
int dfn[MAXN], low[MAXN], ts; // Tarjan算法所需的时间戳数组
int stk[MAXN], top; // 手动实现的栈比std::stack稍快
bool isbcc[MAXN]; // 标记节点是否已属于某个v-BCC
std::vector<std::vector<int>> bcc; // 存储所有找到的点双连通分量
void tarjan(int u, int parent) {
dfn[u] = low[u] = ++ts;
stk[++top] = u;
// 对于孤立点若没有边则tarjan函数会直接结束。
// 我们将在main函数中处理这种情况。
for (int v : adj[u]) {
// 在无向图中,(u,v)和(v,u)是同一条边,避免访问刚过来的边
if (v == parent) {
continue;
}
if (!dfn[v]) { // v未被访问是u在DFS树中的子节点
tarjan(v, u);
low[u] = std::min(low[u], low[v]);
// 关键判断v及其子树无法到达u的祖先
if (low[v] >= dfn[u]) {
std::vector<int> curbcc;
int node;
// 从栈中弹出节点直到v被弹出
do {
node = stk[top--];
curbcc.push_back(node);
isbcc[node] = true;
} while (node != v);
// 割点u也属于这个v-BCC
curbcc.push_back(u);
isbcc[u] = true;
bcc.push_back(curbcc);
}
} else { // v已被访问(u,v)是返祖边
low[u] = std::min(low[u], dfn[v]);
}
}
}
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for (int i = 0; i < m; ++i) {
int u, v;
std::cin >> u >> v;
// 题目允许重边和自环。对于v-BCC重边和一条边效果一样自环可特殊处理
// 此处我们正常建图让tarjan处理
if (u == v) continue; // v-BCC定义通常不考虑自环但题目样例有先忽略自环边最后处理孤立点
adj[u].push_back(v);
adj[v].push_back(u);
}
// 遍历所有节点,确保处理完所有连通分量
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) {
// 如果一个节点是孤立的(或只与已访问过的部分相连),
// tarjan调用后栈可能为空或只含自己。
// 为了简化我们只在tarjan内部处理连通的组件。
tarjan(i, 0);
}
}
// 最后的清扫处理所有未被分配到任何v-BCC的节点
// 这些通常是孤立点,或者图中只有一个节点的情况,包括有自环的孤立点
for (int i = 1; i <= n; ++i) {
if (!isbcc[i]) {
bcc.push_back({i});
}
}
// 输出结果
std::cout << bcc.size() << "\n";
for (const auto& ret : bcc) {
std::cout << ret.size();
for (int node : ret) {
std::cout << " " << node;
}
std::cout << "\n";
}
}