强连通 缩点(两次dfs求出所有强连通分量)

poj 1236 (解题报告)

题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

也就是:
—        给定一个有向图,求:
 1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
 2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
 —        顶点数<= 100
解题思路:
—        1. 求出所有强连通分量
—        2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
—        3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少
加边的方法:
要为每个入度为0的点添加入边,为每个出度为0的点添加出边
假定有 n 个入度为0的点,m个出度为0的点,如何加边?
把所有入度为0的点编号 0,1,2,3,4 ....N -1
每次为一个编号为i的入度0点可达的出度0点,添加一条出边,连到编号为(i+1)%N 的那个出度0点,
这需要加n条边
若 m <= n,则
加了这n条边后,已经没有入度0点,则问题解决,一共加了n条边
若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。
所以,max(m,n)就是第二个问题的解
此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加清单的项了,所以答案是1,0;

注意遍历finish的时候是从最后一项开始的,这样才能求出所有强连通分量

#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
#define max 1000
vector <int> up[max];
vector <int> down[max];
vector <int> finish;
bool vis[max];
int out[max], in[max];
int belong[max];
void dfs1 (int i)
{
 int j;
 vis[i] = true;
 for (j = 0;j < up[i].size();j ++)
 {
  if (!vis[up[i][j]])
   dfs1(up[i][j]);
 }
 finish.push_back(i);
}
void dfs2(int i, int c)
{
 int j;
 vis[i] = true;
 belong[i] = c;
 for (j = 0;j < down[i].size();j ++)
 {
  if (!vis[down[i][j]])
  {dfs2(down[i][j], c);}
 }
}
int main ()
{
 int n, i, j, x, cnt, count, count1;
 while (scanf ("%d",&n) == 1)
 {
  for (i = 1;i <= n;)
  {
   scanf ("%d",&x);
   if (x == 0)
   {
    i++;
    continue;
   }
   else
   {
   up[i].push_back(x);
   down[x].push_back(i);
   }
  }
  memset(vis, false, sizeof (vis));
  for (i = 1;i <= n;i ++)
  {
   if (!vis[i])
   {dfs1(i);}
  }
  memset(vis, false, sizeof (vis));
  cnt = 0;
  for (i = finish.size() - 1; i >= 0;i --)
  {
   if (!vis[finish[i]])
   {
    cnt ++;
    dfs2(finish[i], cnt);
   }
  }
  memset (out, 0, sizeof (out));
  memset (in, 0, sizeof (in));
  for (i = 1;i <= n;i ++)
  {
   for (j = 0;j < up[i].size();j ++)
   {
    if (belong[i] != belong[up[i][j]])//不是同一个联通分量
     {
     out[belong[i]] ++;//标记此联通分量有出度
     in[belong[up[i][j]]] ++;//标记此联通分量有入度
     }
   }
  }
  count = 0;
  count1 = 0;
  for (i = 1;i <= cnt;i ++)
  {
   if (in[i] == 0)
   {count ++;}
   if (out[i] == 0)
   count1 ++;
  }
  if (cnt == 1)//只有一个联通分量的时候特判
   printf("1\n0\n");
  else
  {
  int Max = count < count1 ? count1 : count;
  printf("%d\n%d\n",count, Max);
  }
 }
}


你可能感兴趣的:(强连通 缩点(两次dfs求出所有强连通分量))