本篇章的内容我的学习大多已开在算法进阶指南这本书和题解(算法进阶指南中有关搜索树的概念解释的特别好),主要笔记都在算法进阶指南中,代码上传的是一篇题解里面的,这位博主的注释写的特别好
#include
#include
#include
using namespace std;
const int N = 5010, M = 20010;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
int id[N], dcc_cnt;
bool is_bridge[M];//每条边是不是桥
int d[N];//度数
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void tarjan(int u, int from)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u;
for (int i = h[u]; i!=-1; i = ne[i])
{
int j = e[i];
if (!dfn[j])//j未遍历过
{
tarjan(j, i);//dfs(j)
low[u] = min(low[u], low[j]);//用j更新u
if (dfn[u] < low[j])//j到不了u
// 则x-y的边为桥,
//正向边is_bridge[i] 反向边is_bridge[i ^ 1]都是桥
is_bridge[i] = is_bridge[i ^ 1] = true;
// 这里i==idx 如果idx==奇数 则反向边=idx-1 = idx^1
// 如果idx==偶数 则反向边=idx+1 = idx^1
}
// j遍历过 且i不是反向边(即i不是指向u的父节点的边)
// 因为我们不能用u的父节点的时间戳更新u
else if (i != (from ^ 1))
low[u] = min(low[u], dfn[j]);
}
//双连通分量起点u /
// u
// /
// ne1
// ne2
if (dfn[u] == low[u])
{
++ dcc_cnt;
int y;
do {
y = stk[top -- ];
id[y] = dcc_cnt;
} while (y != u);
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while (m -- )
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
tarjan(1, -1);//防止搜反向边 用一个from
for (int i = 0; i < idx; i ++ )
//如果边i是桥 在其所连的出边的点j所在强连通分量的度+1
// 桥两边的双连通分量各+1
if (is_bridge[i])
d[id[e[i]]] ++ ;
int cnt = 0;
for (int i = 1; i <= dcc_cnt; i ++ )
if (d[i] == 1)//多少个度数为1的节点(强连通分量)
cnt ++ ;//需要加的边的数量
cout << (cnt + 1) / 2 << endl;
return 0;
}
#include
#include
using namespace std;
const int N = 1e4 + 10, M = 2 * 1e5 + 10;
int n, m;
int root;
int h[N], ne[M], e[M], idx;
int dfn[N], low[N], timestamp;
int dcc_cnt;
int tree_ans;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void tarjan(int u)
{
int trees = 0;//当前块内可以分出来的子树个数
dfn[u] = low[u] = ++ timestamp;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
if (low[j] >= dfn[u]) trees ++ ;
}
else low[u] = min(low[u], dfn[j]);
}
if (u != root) trees ++;
tree_ans = max(tree_ans, trees);
}
int main()
{
while (cin >> n >> m, n || m)
{
tree_ans = 0;
dcc_cnt = 0;
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(h, -1 ,sizeof h);
idx = 0;
while (m -- )
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
for (root = 0; root < n; root ++ )
{
if (!dfn[root])
{
dcc_cnt ++;
tarjan(root);
}
}
cout << dcc_cnt + tree_ans - 1 << endl;
}
return 0;
}
#include
#include
#include
using namespace std;
const int N = 1010, M = 1010;
int m, n;
int root;
int dfn[N], low[N], timestamp;
int h[N], e[M], ne[M], idx;
vector<int> dcc[N];
int dcc_cnt;
bool cut[N];
int stk[N], top;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void tarjan(int u)
{
low[u] = dfn[u] = ++ timestamp;
stk[ ++ top] = u;
//1.是孤立点
if (u == root && h[u] == -1)
{
++ dcc_cnt;
dcc[dcc_cnt].push_back(u);
//不影响 top -- ;
return ;
}
//2.不是孤立点
int son = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
if (dfn[u] <= low[j])//找到割点了,一个割点属于多个联通块,因此找到割点后立马进行联通块的记录
{
son ++ ;
if (u != root || son > 1) cut[u] = true;
++ dcc_cnt;
int y ;
do{
y = stk[top -- ];
dcc[dcc_cnt].push_back(y);
}while (y != j);
dcc[dcc_cnt].push_back(u);
}
}
else low[u] = min(low[u], dfn[j]);
}
}
int main()
{
int T = 1;
while (cin >> m, m)
{
memset(h, -1, sizeof h);
memset(dfn, 0, sizeof dfn);
memset(cut,0,sizeof cut);
for (int i = 1; i <= dcc_cnt; i ++) dcc[i].clear();
n = dcc_cnt = idx = top = timestamp = 0;//n也要记得初始化,题目没给n但是给了各个点的编号,得自己比对
while (m -- )
{
int a, b;
cin >> a >> b;
n = max(n, a), n = max(n, b);
add(a, b), add(b, a);
}
for (root = 1; root <= n; root ++ )
{
if (!dfn[root]) tarjan(root);
}
int res = 0;//最少新增几个出口
unsigned long long num = 1;//方案数,题目说了2^64以内
for (int i = 1; i <= dcc_cnt; i ++ )
{
int cnt = 0;
//遍历每个联通块里面的点
for (int j = 0; j < dcc[i].size(); j ++ )
{
if (cut[dcc[i][j]] == true)
{
cnt ++ ;
}
}
if (cnt == 0)
{
if(dcc[i].size()>1)res+=2,num*=dcc[i].size()*(dcc[i].size()-1)/2;
else res++;
}
else if (cnt == 1)
{
res += 1;
num *= dcc[i].size() - 1;
}
//cnt >= 2的话不用添加出口
}
printf("Case %d: %d %llu\n", T ++, res, num);
}
return 0;
}