危险系数 蓝桥杯 求两点间割点 tarjan && poj 1523 SPF Apare_xzc

危险系数 蓝桥杯 求两点间割点 tarjan && poj 1523

题面

问题描述
抗日战争时期,冀中平原的地道战曾发挥重要作用。

地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。

我们来定义一个危险系数DF(x,y):

对于两个站点x和y (x != y), 如果能找到一个站点z,当z被敌人破坏后,x和y不连通,那么我们称z为关于x,y的关键点。相应的,对于任意一对站点x和y,危险系数DF(x,y)就表示为这两点之间的关键点个数。

本题的任务是:已知网络结构,求两站点之间的危险系数。

输入格式
输入数据第一行包含2个整数n(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,通道数;

接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条通道;

最后1行,两个数u,v,代表询问两点之间的危险系数DF(u, v)。

输出格式
一个整数,如果询问的两点不连通则输出-1.
样例输入
7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6
样例输出
2

分析

求两点之间的割点

于是我就去学了一下tarjan算法

推荐几篇博客:
http://keyblog.cn/article-80.html
https://www.cnblogs.com/mxrmxr/p/9715579.html


学了tarjan算法之后去做了一下POJ 1523(割点模板题)

题目链接

题意:

给一个无向图,求出所有割点以及去电每个割点后整个图连通分量增加的个数

直接上代码好了:

/*
 POJ 1523 SPF 求割点和每个割点去掉后增加的连通分量的个数 
Run ID	   User	   Problem	Result	  Memory	Time	Language  Code Length	Submit Time
21061713   apare   1523	    Accepted  164K	    0MS	    C++	1     476B	        2019-11-17 09:41:21
*/
#include <iostream>
#include <cstdio>
#include <map>
#include <cstring>
using namespace std;
const int maxn = 1000+50;
const int maxm = 1000000+100;
int head[maxn],tot,cnt;
struct Node{
	int to,Next;
}node[maxm*2];
int dfn[maxn],low[maxn];
void init()
{
	tot = 0,cnt=0;
	memset(dfn,0,sizeof(dfn));
	memset(head,-1,sizeof(head));
}
void addedge(int from,int to)
{
	node[tot].to = to;
	node[tot].Next = head[from];
	head[from] = tot++;
} 
int ca;                        
map<int,int> mp;
map<int,int>::iterator it;
void tarjan(int x,int root,int fa)
{
	dfn[x] = low[x] = ++cnt;
	int child = 0;
	for(int i=head[x];i!=-1;i=node[i].Next)
	{
		int to = node[i].to;
		if(!dfn[to]) //这个节点to还没被访问过 
		{
			++child;
			tarjan(to,root,x);
			low[x] = min(low[x],low[to]);
			if(x!=root&&dfn[x]<=low[to]) 
				++mp[x]; 
		}
		else if(to!=fa) //这个节点已经被访问过了,而且不是父节点 
		{
			low[x] = min(low[x],dfn[to]);
		} 
	}
	if(x==root&&child>=2) mp[x] = child-1;
}
void solve()
{
	mp.clear();
	memset(dfn,0,sizeof(dfn));
	tarjan(1,1,1);
	printf("Network #%d\n",++ca);
	if(!mp.size())
	{ 
		printf("  No SPF nodes\n\n");
		return;
	}
	for(it=mp.begin();it!=mp.end();++it)                   
		printf("  SPF node %d leaves %d subnets\n",it->first,it->second+1);
	printf("\n"); 
}
int main()
{
	int u,v=10;
	init();
	while(scanf("%d",&u)!=EOF)
	{
		if(u==0)
		{
			if(v==0) break;
			solve();
			init();
		}
		else
		{
			scanf("%d",&v);
			addedge(u,v);
			addedge(v,u);		
		}	
		v = u;
	}
	
	return 0;
}

会了tarjan算法以后我们再来看危险系数这道题

题意:求无向图中给定两点之间的割点数

ps:参考了网上几乎所有博客,大多位dfs暴力枚举或者暴力并查集
  主要参考这篇文章

思路:

  • 先找出图的所有割点,然后分别判断是否满足为两点st,ed的割点
  • 一个点是两点间的割点要满足几个条件:
  1. 该点可达ed
  2. 至少一个子节点可达ed回不去更早的节点

代码:

/*
危险系数 xzc
给定一个图,求两个点之间割点的个数 
许智超	危险系数	11-18 17:16	1.665KB	C++	正确	100	0ms	16.23MB
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000+50;
const int maxm = 2000000+50;
int head[maxn],tot,st,ed;
int cnt,dfn[maxn],low[maxn];
bool reachEd[maxn];
set<int> ans;
set<int>::iterator it;
struct Node{
	int to,Next;
}node[maxm];
void initEdge()
{
	tot = 0;
	memset(head,-1,sizeof(head));
}
void addedge(int from,int to)
{
	node[tot].to = to;
	node[tot].Next = head[from];
	head[from] = tot++;
}
void init()
{
	cnt = 0;
	memset(dfn,0,sizeof(dfn));
	memset(reachEd,false,sizeof(reachEd));
	ans.clear();
}
void tarjan(int x,int root,int fa_x)
{
	dfn[x] = low[x] = ++cnt;
	int child = 0;
	for(int i=head[x];i!=-1;i=node[i].Next)
	{
		int to = node[i].to;
		if(!dfn[to])
		{
			++child;
			tarjan(to,root,x);
			low[x] = min(low[x],low[to]);
			if(to==ed||reachEd[to]) reachEd[x] = true;
			if(x==root&&child>=2) ans.insert(x);
			if(x!=root&&low[to]>=dfn[x]) ans.insert(x);
		}
		else if(to!=fa_x)
		{
			low[x] = min(low[x],dfn[to]);
		}
	}
}
int main()
{	
	int m,n,u,v,ca=0;
	while(cin>>n>>m)
	{
		initEdge();
		for(int i=0;i<m;++i)
		{
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		scanf("%d%d",&st,&ed);
		if(ca++)
		{
			init();
		}
		tarjan(st,st,st);
		reachEd[ed] = true;
		int res = 0;
		for(it=ans.begin();it!=ans.end();++it)
		{
			int x = *it;
			if(x==ed||x==st||!reachEd[x]) continue; //起点和终点都不算两点之间的割点 
			int add = 0;
			for(int i=head[x];i!=-1;i=node[i].Next)
			{
				int to = node[i].to;
				if(low[to]>=dfn[x]&&reachEd[to])
				{
					add = 1;break;
				}
			}
			res += add;
		}
		printf("%d\n",res);
	}
	
	
	return 0;
}
发布了73 篇原创文章 · 获赞 50 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40531479/article/details/103134373