关于图的存储:
邻接表
定义
int h[N * 2]; //当每个节点作为头节点时,指向的是当前点所连接的所有点的指针
int ne[N * 2];//当前下标的下一个下标
int e[N * 2]; //用来存储元素
int idx; //指针
解释
每个h对应的是当前点连接的所有和它距离为1的点,所以ne指向的是下一个点,-1就是没有点了
加边
void add(int a, int b)
{
e[idx] = b; //给b开一个指针
ne[idx] = h[a]; //将idx的下一个指向a原本的下一个
h[a] = idx;//h指向idx
//上两步操作实现了将idx插在h[a]的后面,就是头插法
idx ++;
}
初始化
都初始化为-1,也就是说刚开始的时候每个点没有连接任何边。
memset(h, -1, sizeof h);
关于图的深搜
代码
void dfs(int u)
{
st[u] = true;//标记为已经走过
// 从当前节点开始,走向下一个点,下一个不为-1
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];//把这个点上的值取出来
if (!st[j])//如果这个点没有走过
{
//那为什么走的是这个取出来的值呢,详见下面的解释
int s = dfs(j);//那么就要走这个点
}
}
return;
}
上面的解析
因为每个h[a] 接上 ne 后面一长串存的是和 a 点下面的所有点(就是a点下面一层的点)
然后下面每个点又作为头结点存储了和它连接的点,
因为h[点值] = 下一个点的下标,
所以当我们走到一个点可以继续走时,我们要往下走,那么就是要进入下一层,所以取下一个点值作为头结点,才能往下走。
!
关于树的重心
题目
题目解读
一棵树有 n 个节点,n条无向边(也就是说加边的时候要加两条)。
它要求找的是,删除每个点后得到的所有连通块中的最大值,每个点的最大值中再取一个最小值,问这个最小值是多少。
做法
用深搜
返回的是sum,sum 赋初值为1,代表的是以当前点为根节点的子树的大小,res是记录的是删除当前点可以得到的所有连通块的最大值。
ans 是在每次跑玩了当前点的所有子树之后,比较一下最小值。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
bool st[N];
int h[N * 2], ne[N * 2], e[N * 2], idx;
int n;
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx ++;
}
int ans = 1e9 + 10;
int dfs(int u)
{
st[u] = true;
int res = 0, sum = 1;
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
int s = dfs(j); //s 用来存储返回的值,也就是这块连通块的值
res = max(res, s);//取最大的连通块
sum += s; //sum 加上s是为了计算出以当前节点为根的子树的总大小,方便后面的返回和取出最后一块连通块
}
}
res = max(res, n - sum);//和最后一块连通块比大小
ans = min(ans, res); //和所有的最大值比最小值
return sum; //返回该子树的总大小
}
int main()
{
memset(h, -1, sizeof h);
cin >> n;
for (int i = 1; i < n; i ++ )
{
int a, b;
cin >> a >> b;
add (a, b);
add (b, a);
}
dfs(1);
cout << ans << endl;
return 0;
}
手写解析
总结
总之就是突然就悟了,这不是一个巧合,而是厚积薄发, 但好害怕睡一觉,然后又给忘了。
但是从未有一刻像现在这样通透过!