当前位置: 代码迷 >> 综合 >> 【Network of Schools】tarjan算法与图的连通性
  详细解决方案

【Network of Schools】tarjan算法与图的连通性

热度:20   发布时间:2024-01-04 11:53:24.0

A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B 
You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school. 

Input

The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.

Output

Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

Sample Input

5
2 4 3 0
4 5 0
0
0
1 0

Sample Output

1
2

题意:

每个学校都可以把软件复制好,交给它名单上的学校。

问题A:把软件复制成几份,然后交给不同的学校,所有学校才能够都有软件。

问题B:添加几条边,能使得这个图变成强连通图。

分析:

把学校看作节点,若学校A能支援学校B,则从A->B建边,得到一张有向图,在一个图的强连通分量重,任意两个点可达,因此,只要这个强连通分量中的任意一个点获得软件,该强连通分量内的其他所有学校都可以获得。

因此,可以用tarjan算法求出所有的强连通分量,并“缩点”,得到一张有向无环图。“零入度的学校无法被其他学校支援”,因此即为第一问的答案。第二问:最少添加几条边能把一张任意的有向图变成强连通图。画图可知:ans=max(“零入度点”,“零出度点”)。

trick:

1.若缩点后本身就是一个强连通图,第二问答案为0

2.第二问要取max

3.栈注意清空

 

附上tarjan代码:

#pragma warning(disable:4996)
#include<algorithm>
#include<cstdio>
#include<string>
#include<queue>
#include<math.h>
#include<map>
#include<stdlib.h>
#include<stack>
#include<bitset>
#include<string.h>
#include<iostream>
using namespace std;typedef pair<int, int> pii;
#define ll long long
#define CLR(a,b) memset(a,0,sizeof(a))
const int mod = 1e9 + 7;
const int maxn = 1e2 + 6;
const double eps = 1e-8;
const double pi = acos(-1.0);struct node {int x, y;node() {}node(int _x, int _y) :x(_x), y(_y) {}
};
priority_queue<pii, vector<pii>, less<pii> >q;int dfn[maxn];//dfn[i]表示dfs在第一次搜到的序(时间戳)
int low[maxn];//low[i]表示该i或i的子树中最小的时间戳
int vis[maxn];//用于表示是否在栈中
int c[maxn];//c[i]表示i所属的新的联通块
vector<int>v[maxn];//原图建边
vector<int>SCC[maxn];//SCC[i]表示第i个强连通分量的点
vector<int>newv[maxn];//新建的有向无环图
int du1[maxn],du2[maxn];//出度,入度
stack<int>st;//堆栈
int tot = 0;//时间戳
int ans = 0;//强连通分量数void tarjan(int x){dfn[x] = low[x] = ++tot;//为节点x设定次序编号和low初值st.push(x);//将x压入堆栈中vis[x] = 1;for (int i = 0; i < v[x].size(); i++) {//枚举每一条可达边int u = v[x][i];if (!dfn[u]) {//如果节点未访问过tarjan(u);//继续往下找low[x] = min(low[x], low[u]);取自己以及子树的最小low[]}else if (vis[u]) {//如果被访问了,并且还在堆栈中,没有归类到任一强连通分量中low[x] = min(low[x], dfn[u]);//}}if (low[x] == dfn[x]) {//更新完low,如果节点是强连通分量根ans++;while (1) {//将x以及该强连通分量的所有点退栈int t = st.top();st.pop();vis[t] = 0;c[t] = ans;SCC[ans].push_back(t);if (t == x) {break;}}}
}int main() {int n;scanf("%d", &n);for (int i = 1; i <= n; i++) {int x;while (scanf("%d", &x), x) {v[i].push_back(x);}}	for (int i = 1; i <= n; i++) {if (!dfn[i]) {tarjan(i);}}for (int i = 1; i <= n ; i++) {for (int j = 0; j < v[i].size(); j++) {int u = v[i][j];if (c[i] != c[u]) {newv[i].push_back(u);du1[c[u]]++;du2[c[i]]++;}}}int cnt1=0, cnt2 = 0;for (int i = 1; i <= ans ; i++) {if (!du1[i])cnt1++;if (!du2[i])cnt2++;}printf("%d\n%d\n", cnt1, ans==1?0:max(cnt2,cnt1));
}

 

  相关解决方案