题意:
给出你一个图,代表许多学校的网络连接情况,第一个问题:要想把一个信息传输到所有节点至少要在几个节点上放置信息。第二个问题:想要把整个图变成强连通图需要增加多少条边。
思路:
对于第一个问题,找到所有的强连通分量,然后把其缩成一个点,然后再看整个图,统计一下入度为0的点,就是答案(PS:注意整个图是强连通图,也就是入度为0的点不存在,这时应该输出1)。
对于第二个问题,还是和每个节点的度有关系(缩点之后的),统计出度为0的点的数量和入度为0的点的数量,我们要保证取缩点之后不存在出度为0或入度为0的点,所以取二者最大值即为答案(我们可以在入度为0和出度为0的点之间建边,多余的点随便拉一条边接到其他节点上就行)。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1020;
int n, tot, num, top, sum;
int head[maxn], Stack[maxn], dfn[maxn], color[maxn];
int low[maxn], degree_in[maxn], degree_out[maxn];
struct node
{
int v, next;
}edge[maxn * 10];
inline void add(int u, int v) {
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot++;
}
inline void Init() {
top = 0;
sum = 0;
num = 0;
tot = 0;
memset(head, -1, sizeof(head));
memset(dfn, -1, sizeof(dfn));
memset(degree_in, 0, sizeof(degree_in));
memset(degree_out, 0, sizeof(degree_out));
}
void Tarjan(int u)
{
dfn[u] = low[u] = ++num;
Stack[++top] = u;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if(dfn[v] == -1) { //如果没有被访问过
Tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(color[v] == 0) { //如果被访问过,但是不是其他强连通分量的一部分
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]) { //找到一个强连通分量
color[u] = ++sum; //重新编号,相当于缩点
while(Stack[top] != u) {
color[Stack[top--]] = sum;
}
top --;
}
}
int main()
{
//freopen("in.txt", "r", stdin);
cin >> n;
Init();
for(int i = 1; i <= n; ++ i) {
int v;
while(1) {
scanf("%d", &v);
if(v == 0) break;
add(i, v);
}
}
for(int i = 1; i <= n; ++ i) { //不一定是连通图
if(dfn[i] == -1) Tarjan(i);
}
for(int i = 1; i <= n; ++ i) {
for(int j = head[i]; j != -1; j = edge[j].next) {
int v = edge[j].v;
if(color[v] != color[i]) { //不是一个强连通分量的话就连接,但是没有实际操作,只是记录一下出度和入度,有需要的话可以直接连接
degree_in[color[v]] ++;
degree_out[color[i]] ++;
}
}
}
int in = 0, out = 0;
for(int i = 1; i <= sum; ++ i) {
if(degree_in[i] == 0) in++;
if(degree_out[i] == 0) out++;
}
if(sum == 1) //注意整个图都是强连通图的话要特判一下
printf("1\n0\n");
else
printf("%d\n%d\n", in, max(in, out));
return 0;
}