蓝桥杯 危险系数

题意就是求图中两点之间的割点的数目。

不知道被谁指导的说求割点可以用tarjan算法,就用了tarjan算法,但是tarjan算法求的是整个图的割点个数啊,至于用tarjan怎么求两点间的割点就不知道了。做了N多努力也是没求出来。然后就搜了题解,搜题解的时候遭受到大神碾压,某大神在blog里是这么说的“dfs,水题,秒过”TUT,然后痛定思痛决定看看大神怎么写的。具体思路就是,从开始点进行dfs,记录经过的路径,然后到了终点,就将路径里的点弄到另一个数组里,最后判断路径个数和点出现的次数是不是相等,如果相等就说明去掉这个点,任何路径都无法到达了。

#include<iostream>
#include<stdio.h>
#define N 1001
using namespace std;
typedef struct Edge {
	int v;
	struct Edge* next;
} Edge;
Edge Head[N];
int n,m;
int start,end;
bool visited[N];
int way[N];
int cp_count[N];
int way_count;
int cp_num;
bool flag[N];
void dfs(int v,int index) {
	flag[v]=1;
	visited[v]=1;
	//将该点存入路径中 
	way[index]=v;
	//如果这个点是终点 
	if(v==end) {
	//就将这个路径中所有点的次数加一 
		for(int i=0; i<=index; i++)
			cp_count[way[i]]++;
	//路径数也加一 
		way_count++;
	}
	for(Edge* i=Head[v].next; i; i=i->next)
		if(!visited[i->v]) {
			dfs(i->v,index+1);
			//这个比较重要,就是一定要把遍历过的点再弄成没有遍历的,因为下一个路径也有可能经过它 
			visited[i->v]=0;
		}
}

int main() {
	cin>>n>>m;
	for(int i=0; i<m; i++) {
		int x,y;
		cin>>x>>y;
		Edge *tp1,*tp2;
		tp1=new Edge;
		tp2=new Edge;
		tp1->v=y;
		tp1->next=Head[x].next;
		Head[x].next=tp1;
		tp2->v=x;
		tp2->next=Head[y].next;
		Head[y].next=tp2;
	}
	cin>>start>>end;
	dfs(start,0);
	//循环判断路径中的点的出现次数和路径个数 
	for(int i=1; i<=n; i++)
		if(cp_count[i]==way_count)
			cp_num++;
	//感觉这里有点笨了,用flag数组来标识从start点开始遍历过的点,如果从start开始 
	// 没有遍历过end说明这俩点就不连同 
	if(flag[end]==0) cout<<-1;
	else//这里减二是因为cp_num也把start和end算进来了 
		cout<<cp_num-2;

	return 0;
}


顺便贴上用tarjan算法记录图割点数量的代码,从错误中学到的东西总比成功要多。

#include<iostream>
#include<stdio.h>
#define N 1001
using namespace std;
typedef struct Edge {
	int v;
	struct Edge* next;
} Edge;
Edge Head[N];
int parent[N];
int low[N],dfn[N];
int count;
bool cutpoint[N];
bool flag;
int n,m;
int start,end;

void tarjan(int u) {
	int children=0;
	low[u]=dfn[u]=++count;
	for(Edge* i=Head[u].next; i; i=i->next) {
		int v=i->v;
		if(!dfn[v]) {
			children++;
			parent[v]=u;
			tarjan(v);
			low[u]=min(low[u],low[v]);
			//如果是父节点同时子树多于一个 ,那么这个点的父节点就是割点
			if(parent[u]==0 && children >1) cutpoint[u]=1;
			//如果是非父节点,同时这个点的最近祖先节点比其父节点靠后,那么它的父节点就是割点
			if(parent[u]!=0 && low[v]>=dfn[u])  cutpoint[u]=1;
		}//如果有回边,就是这个点指向的不是它的父节点而是它的祖先节点
		else if(v!=parent[u]) {
			low[u]=min(low[u],dfn[v]);
		}


	}
}

int main() {
	cin>>n>>m;
	for(int i=0; i<m; i++) {
		int x,y;
		cin>>x>>y;
		Edge *tp1,*tp2;
		tp1=new Edge;
		tp2=new Edge;
		tp1->v=y;
		tp1->next=Head[x].next;
		Head[x].next=tp1;
		tp2->v=x;
		tp2->next=Head[y].next;
		Head[y].next=tp2;
	}
	cin>>start>>end;
	//这是用tarjan算法求割点的个数,但是我觉得并不适合这个题
	tarjan(start);
	cutpoint[start]=0;
	cutpoint[end]=0;
	for(int i=1; i<=n; i++)
		if(cutpoint[i]) cp_num++;
	if(!dfn[end])
		cout<<-1;
	else
		cout<<cp_num;
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值