传送门啦
其实这个题只要读懂分析好题意就不是很难。
就是将一个有向图进行缩点操作,把一个强连通分量看成一个点,求入度为 0 的点和出度为 0 的点各有多少。
在这里先向大家推荐两个题目,建议大家先去看看那两个题:
1.[p2002消息扩散](https://www.luogu.org/problemnew/show/P2002)
这是强连通分量与入度关系的例题
2.[p2341受欢迎的牛](https://www.luogu.org/problemnew/show/P2341)
这是强连通分量和出度关系的题目
这两个题算是本题的一个基础题,强烈建议大家看看。
当然我也有相应的博客
p2002消息扩散:
https://xxwang.blog.luogu.org/xiao-xi-kuo-san
p2341受欢迎的牛:
https://xxwang.blog.luogu.org/shou-huan-ying-di-niu
只是觉得这两个题比较好,仅此而已。。
回归正题:
一、
问题 1 是选取最少数量的母机来使每个学校都用上。
对于这个问题不难想求出所有入度为 0 的点
用同样的方法证明一下:
1.充分性证明:如果入度为 0 的点不是信息出发点,那么这个点必定不会接收到任何节点发出的信息,因为它的入度为 0 。
2.必要性证明:如果信息出发点不是入度为0的点,那么其必有入度点,使其覆盖点更多,而我们要找至少多少个点。
二、
问题 2 是至少要添加几条线路能使任意一所学校作为母机都可以使别的学校用上
可以自己手动模拟一下,要想线路最少,就把一个出度为 0 的点**不重复的**连到一个入度为 0 的点上。(以上的点都是缩点之后的)
不难发现所求的边数就是出度为 0 的点的个数。。
不知道要不要再发一遍数组意思了,就假设你们没看过我那两篇题解
首先是tarjan缩点中的几个数组:
dfn[i]:i点的时间戳
low[i],表示这个点以及其子孙节点连的所有点中dfn最小的值
stack[],表示当前所有可能能构成是强连通分量的点。
ins[i],表示 i 是否在stack[ ]数组中
num[i],表示第 i 个强连通分量中有多少个点
belong[i],表示第 i 点在哪一个强连通分量里
然后是两个处理数组:
in[i]:表示第 i 个**强连通分量**的入度
out[i]:表示第 i 个**强连通分量**的出度
最后直接贴上代码了,感觉自己的码风还是挺好懂的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 10005;
const int maxm = 5e6 + 6;
int read(){
char ch = getchar();
int f = 1 , x = 0;
while(ch > '9' || ch < '0'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n,m,s;
int head[maxn],tot;
int dfn[maxn],low[maxn],ind;
int stack[maxn],top,num[maxn],belong[maxn],cnt;
bool ins[maxn];
int in[maxn],chu[maxn],ans1,ans2;
struct Edge{
int from,to,next;
}edge[maxm];
void add(int u , int v){
edge[++tot].from = u;
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot;
}
void tarjan(int x){
low[x] = dfn[x] = ++ind;
ins[x] = true;
stack[++top] = x;
for(int i=head[x];i;i=edge[i].next){
int v = edge[i].to;
if(ins[v]) low[x] = min(low[x] , dfn[v]);
if(!dfn[v]) {
tarjan(v);
low[x] = min(low[x] , low[v]);
}
}
int k = 0;
if(dfn[x] == low[x]){
cnt++;
do{
k = stack[top];
num[cnt]++;
top--;
ins[k] = false;
belong[k] = cnt;
} while(k != x);
}
}
int main(){
n = read();
for(int i=1;i<=n;i++){
while((s = read()) != 0){
m++;
add(i , s);
}
}
//printf("%d
",m);
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=m;i++)
if(belong[edge[i].from] != belong[edge[i].to]){
in[belong[edge[i].to]]++;
chu[belong[edge[i].from]]++;
}
for(int i=1;i<=cnt;i++){
if(in[i] == 0) ans1++;
if(chu[i] == 0) ans2++;
}
printf("%d
%d",ans1,ans2);
return 0;
}