题目
题解
DFS。
蓝桥杯中,一般看到图不是BFS就是DFS。
代码1对应第一种方法,我的方法:
根据关键点的定义:删除这个点之后,无法实现从u
到v
。
那么我们就枚举每个点作为删除点,判断删除这个点之后还能不能实现从u
到v
。若不能说明删除的点为关键点,否则非关键点。
在代码实现的时候,我想把删除点的标记数组与已访问的标记数组统一成一个,但是发现搜索的时候存在将已访问结点进行标记和恢复操作,这也影响到了删除点的标记,实际上删除点的标记应该是一成不变的。所以还是要将两个数组分开。
更新于2022.4.2
实现代码时定义一个del变量表示被删除的点的编号,dfs遇到直接continue。
枚举被删除的点时只排除终点,也就是说也存在删除起点的情况,因为我们需要先判断起点和终点是否连通,删除起点相当于其他点一个都没删除,如果在这种情况下都无法保证存在一种到达终点的情况,那么说明不连通,输出-1,其他情况正常输出统计的个数。
代码2对应第二种方法,也是网上比较多的方法:
这种方法的思想:关键点就是每条从u
到v
的路径都包含的点就是关键路径,或者说某个点在在路径中的出现次数与路径总数相等就是关键点。
正常的从起点DFS,到达终点就将当前保存的路径上全部点对应于保存点出现次数的数组中相应的值加一。跑完DFS后,遍历每个点(除起点和终点)判断次数是否为路径总数,是则答案加一。
邻接表还是要学学的,稀疏图好用,其实不理解可以只背代码。
代码1
代码更新于2022.4.2
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+10;
int vis[N], e[N<<1], ne[N<<1], h[N];
int n, m, flag, u, v, a, b, idx, ans, del;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int x) {
if(x == v || flag) {flag = 1; return ;} // 若到达终点 或 到达过终点,直接返回就行了,因为我们已经得到flag标记了
for(int i = h[x];i != -1;i = ne[i]) {
int y = e[i];
if(vis[y] || del == y) continue; // 被访问过 或 是被删除的点
vis[y] = 1;
dfs(y);
vis[y] = 0;
}
}
int main()
{
cin >> n >> m;
memset (h, -1, sizeof h);
for(int i = 1;i <= m;i ++) cin >> a >> b, add (a, b), add (b, a);
cin >> u >> v;
for(int i = 1;i <= n;i ++) { // 枚举删除哪个点
if(i != v) { // 排除终点
memset (vis, 0, sizeof vis);
vis[u] = 1;
flag = 0;
del = i;
dfs(u);
if (!flag) ans++; // 如果删除结点i后,无法实现u->v,说明删除的这个点i是关键点
}
}
if (!ans) cout << -1 << endl;
else cout << ans << endl;
return 0;
}
代码2
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+10;
int vis[N], e[N<<1], ne[N<<1], h[N], node[N], path[N];
int n, m, u, v, a, b, idx, ans, paths;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int x, int step) {
if(x == v) {
for(int i = 1;i <=step;i ++) // 将路径上的点的经过次数都++
path[node[i]] ++;
paths ++; // u->v 的路径总数
return ;
}
for(int i = h[x];i != -1;i = ne[i]) {
int y = e[i];
if(vis[y]) continue;
vis[y] = 1;
node[step+1] = y; // 记录路径结点
dfs(y, step+1);
vis[y] = 0;
}
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h); // !
for(int i = 1;i <= m;i ++) cin>>a>>b, add(a, b), add(b, a);
cin>>u>>v;
vis[u] = 1; // !
dfs(u, 1);
for(int i = 1;i <= n;i ++)
if(path[i] == paths && i!=u && i!=v) ans ++; // 排除起点和终点
cout << ans << endl;
return 0;
}