感觉应该没写错吧.
// dfs 框架
/*
void dfs(int u){
st[u]=true; // 标记一下,记录为已经被搜索过了,下面进行搜索过程
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(!st[j]) {
dfs(j);
}
}
}
*/
首先要深入理解数组模拟的邻接表含义
idx 就是index坐标/位置. 比如头节点2存在坐标h[2]=-1,第一条边2->4,4存在0坐标e(0).., 边2->4信息,由ne[0]=-1表示. 插入一次后目前该链的头在h[2]=0.
e[idx] element 元素
ne[idx] next 指针
h[N] : 表示 第 i 个节点的 第一条边的 idx
ne[M] : 表示 与 第 idx 条边 同起点 的 下一条边 的 idx
e[M] : 表示 第idx 条边的 终点
N : 节点数量
M:边的数量
i : 节点的下标索引
idx : 边的下标索引
这两个基本就是核心,e是这条边连着/指向的的节点值,ne是表示这条边起始点即上一节点。
比如2->4 就是e(0)..4, ne(0)..-1(节点2的h初始化位置就是-1)
再加一条边变成2->1->4 就是e(1)..1, ne(1)=0. idx=1的上一节点是idx=0,也就是上一行的e(0)=4
以此类推。
就是idx++产生的
..... e(i=0) e(i=1)..
ne(i=0)=-1. 0. 1. 2
h(u) ..= 0. 1. 2. 3
//背景知识: 邻接表a->b, add(a,b): e[idx] = b, ne[idx] = h[a], h[a] = idx++; h =-1,idx =0初始化.
int dfs(int u){
st[u] = true;
int sum = 1;//子树的节点各数
int res = 0;
for (int i = h[u]; i != -1; i=ne[i]){ // (* 见下方)
int j = e[i]; //邻接表性质, e存着所有节点.idx=0,1,2,3...e[0]第一个插入的边(也就是与之相连的第一个节点,此时共2个节点,以此类推)
if (!st[j]){
int s = dfs(j); //当前j为根的子树大小,图中的size值
res = max(res, s); //res设定为最大联通子树的节点数, 顾每个子树都比一下。
sum += s; //j为根子树大小,这个点遍历过了,所以子树大小从1个节点加到2个节点,一直加.
}
}
res = max(n - sum, res); //整个图可能还有一个树指向该节点,所以也要比一下那一块(子树)的节点数量和我们这一个/两个. 即为图中n-size
ans = min(ans, res); //要取所有重心里最小的(最大连通块)数
return sum; //u为根子树大小(含自身)
}
(*)原理:
递归, 如果这个点没走过,就把它走一下st[u] = true;
开始走:进入下一个节点(上一个点, 也就是next指针指向的节点比如a->b, 就是b上一个点a.)
《=》也就是需要标记数组st[N], 遍历节点u的每个相邻的边
for (int i = h[u]; i != -1; i=ne[i]) //从节点u的头节点开始, 向后遍历每个联通的节点.i=ne[i], 直到抵达第一个点ne[i]=0
比如2->5->3->1->4
h[2]3..2..1..0..-1
ne[i]..2..1..0..-1
就是ne[3]=2(下个点在序号2),以此类推. 所以到初始值i=ne[0]=h[2]=-1 (h=-1)停下不再进入循环.
这里很好理解,因为插入add(a,b)时是从ne(0)=h[2]++=-1, ne(1)=h[2]=0,一路插进来的。
随后,
设定dfs(u) 是以当前u节点的子树的大小(节点数量).
那么遍历一遍完成设定.(按设定规则搜一遍)
如果没搜过, 继续搜一下
if (!st[j]){
dfs(j)
}
按设定,搜的时候,比较大小,res 设为删除某个节点后,其最大连通子图节点数量.
res = max(res, s);
最后由于当作双向图来做,add(a,b), add(b,a), 所以可以以任意点开始深搜,每个点都相连
dfs(1); // dfs(2) 都行, 但要小于最大节点n,也不能从0开始,因为节点从1开始。 一共就n个点,你搜n+1节点就是不相连的空节点了
More is coming 稍后写其他题.
Permutation 排列组合. 给定数字n,输出所有组合情况,
1 2 3
1 3 2
...
void dfs(int u){
if (u == n){
for (int i = 0; i < n; i++)
printf("%d ", res[i]);
puts("");
} else {
for (int i = 1; i <= n; i ++ ){
if (state[i] == false){
res[u] = i; //由于下面是dfs(u+1), 所以为了能够在第index位做到枚举,这里是u。
state[i]=true;
dfs(u + 1);
state[i]=false; //回溯.因为1 3 2, 1 2 3 完成后, 2 ..开始的state[1, 3] 还需要进行判断。
}
}
}
}
为什么res[u] 是u: (答:由于dfs特性dfs(u+1),u是答案数组的index)
因为dfs(u = 0):
...dfs(u+1); 所以就成了index
换一种方式,u改成index
更好理解
int dfs(int index)
{
if (index == n){
for (int i = 0; i < n; i++)
printf("%d ", res[i]);
puts("");
}
else{
for (int i = 1; i <=n; i++){
if (state[i] == 0){
res[index] = i;
//printf("%d",index);
//puts("x");
state[i] = 1;
dfs(index + 1);
state[i] = 0;
}
}
}
}
为了输出第0位,第1位,第2位三个数(答案),
因为后面一直u++直到u=n break (比如u=3),
配合i = 1; i<=n;i++
即可i=1 循环一遍res[0 1 2];i=2->i=3一条路返回, i=3->i=2一条路再返回,
i = 2 循环一遍res[0 1 2]; i =1->i=3一条路返回, i=3->i=1一条路返回,
i = 3 循环一遍res[0 1 2]; i =1->i=2一条路返回, i=2->i=1一条路返回,
结束
dfs(u):
for (i <= n)
res[u] = i;
dfs(u+1);
int main(){
dfs(0);
}
// 这样第一次进来u = 0, res[0] = 1,i=2 res[1]=2, i=3 res[2]=3 然后到递归栈底回溯
state[3]=0,再回溯state[2]=0 进入i = 3, 1 3 2,回溯state[2]=0, state[3]=0.
// res[1] = 2,