[蓝桥杯][2013年第四届真题]危险系数

题目

题目链接

题解

DFS。
蓝桥杯中,一般看到图不是BFS就是DFS。


代码1对应第一种方法,我的方法:

根据关键点的定义:删除这个点之后,无法实现从uv
那么我们就枚举每个点作为删除点,判断删除这个点之后还能不能实现从uv。若不能说明删除的点为关键点,否则非关键点。

在代码实现的时候,我想把删除点的标记数组与已访问的标记数组统一成一个,但是发现搜索的时候存在将已访问结点进行标记和恢复操作,这也影响到了删除点的标记,实际上删除点的标记应该是一成不变的。所以还是要将两个数组分开。

更新于2022.4.2
实现代码时定义一个del变量表示被删除的点的编号,dfs遇到直接continue。

枚举被删除的点时只排除终点,也就是说也存在删除起点的情况,因为我们需要先判断起点和终点是否连通,删除起点相当于其他点一个都没删除,如果在这种情况下都无法保证存在一种到达终点的情况,那么说明不连通,输出-1,其他情况正常输出统计的个数。


代码2对应第二种方法,也是网上比较多的方法:

这种方法的思想:关键点就是每条从uv的路径都包含的点就是关键路径,或者说某个点在在路径中的出现次数与路径总数相等就是关键点。

正常的从起点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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值