洛谷 P5058 [ZJOI2004]嗅探器 【tarjan+bfs+割点】

本文针对洛谷P5058题目提供了解决方案,通过使用tarjan算法寻找割点,确保在网络中安装嗅探器后,两个信息中心无法直接通信。文章详细解释了算法思路,并附上了完整的AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


传送门:洛谷 P5058



题目描述
某军搞信息对抗实战演习,红军成功地侵入了蓝军的内部网络,蓝军共有两个信息中心,红军计划在某台中间服务器上安装一个嗅探器,从而能够侦听到两个信息中心互相交换的所有信息,但是蓝军的网络相当的庞大,数据包从一个信息中心传到另一个信息中心可以不止有一条通路。现在需要你尽快地解决这个问题,应该把嗅探器安装在哪个中间服务器上才能保证所有的数据包都能被捕获?

输入格式
输入文件的第一行一个整数 n,表示蓝军网络中服务器的数目。

接下来若干行是对蓝军网络的拓扑结构描述,每行是两个整数 i , j 表示编号为 i 和编号为 j 的两台服务器间存在连接(显然连接是双向的),服务器的编号从 1 开始,一行两个 0 表示网络的拓补结构描述结束,再接下来是两个整数 a , b 分别表示两个中心服务器的编号。

输出格式
输出编号。如果有多个解输出编号最小的一个,如果找不到任何解,输出 No solution

输入输出样例
输入 #1
5
2 1
2 5
1 4
5 3
2 3
5 1
0 0
4 2
输出 #1
1

说明/提示
1≤n≤100



题解:

题目可理解为,在图中去掉一个点,使这个原本是一个强连通分量的图变成几个图,且两个信号点不能在一个强连通分量中。
这样的话,就很明显是一个tarjan求割点的问题,那么我们现在的问题就是,如何找到最小的割点,使两个信号点互不相通。

找最小割点我们可以用set来存所有割点,注意到题目的数据范围n最大才100,就想到可以搜索。
从第一个信号点出发,割去割点(即不经过割点),如果仍然可以到达第二个信号点,说明这个割点不符合要求;反之,如果不能到达第二个信号点,说明符合要求。
因为割点用set存,已经排过序,所有一旦搜到直接输出即可。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=11000;
int n,f,t,k,idx;
int dfn[N],low[N],head[N],vis[N];
set<int> s;
struct edge
{
  int to,next;
}e[2*N];
void init()
{
  memset(vis,0,sizeof(vis));
}
void add(int u,int v)
{
  e[k].to=v;
  e[k].next=head[u];
  head[u]=k++;
}
void tarjan(int u,int fa)
{
  int child=0;
  dfn[u]=low[u]=++idx;
  for(int i=head[u];i!=-1;i=e[i].next)
  {
    int v=e[i].to;
    if(!dfn[v])
    {
      tarjan(v,u);
      low[u]=min(low[u],low[v]);
      if(low[v]>=dfn[u]&&u!=fa)
      {
        s.insert(u);
      }
      if(u==fa)
      {
        child++;
      }
    }
    low[u]=min(low[u],dfn[v]);
  }
  if(u==fa&&child>1)
  {
    s.insert(u);
  }
}
void bfs(int x,int id)
{
  queue<int> q;
  vis[x]=1;
  q.push(x);
  while(!q.empty())
  {
    int p=q.front();
    q.pop();
    for(int i=head[p];i!=-1;i=e[i].next)
    {
      int tmp=e[i].to;
      if(vis[tmp]) continue;
      if(tmp!=id)
      {
        vis[tmp]=1;
        q.push(tmp);
      }
    }
  }
}
int main()
{
  memset(head,-1,sizeof(head));
  k=1,idx=0;
  scanf("%d",&n);
  int x,y;
  while(scanf("%d%d",&x,&y)&&x&&y)
  {
    add(x,y);
    add(y,x);
  }
  for(int i=1;i<=n;i++)
  {
    if(!dfn[i])
    {
      tarjan(i,i);
    }
  }
  scanf("%d%d",&f,&t);
  set<int>::iterator it;
  for(it=s.begin();it!=s.end();it++)
  {
    init();
    bfs(f,*it);//从第一个信号器出发,如果不走*it,会经过哪些点
    if(!vis[t])//如果不会走到第二个信号器,那么说明*it是我们所要的割点
    {
      printf("%d\n",*it);
      return 0;
    }
  }
  printf("No solution\n");
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值