版权声明:Dream_dog专属 https://blog.csdn.net/Dog_dream/article/details/84671583
题意:一张有向图,一问至少给几个点发送软件,才能让所有点都能收到软件;二问是至少添加几条边才能让整个图是一个连通分量;
题解:先求连通分量然后再把同一个连通分量的点缩为一个点,再求所有点的入度和出度 答案一就是入度 答案二就是max(in,out) 注意:当只有一个连通分量时答案为0
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define il inline
typedef long long ll;
const int maxn = 100 + 2;
const int minn = 130 + 1;
struct Edge {
int to, nxt;
Edge() {}
Edge(int to, int nxt) :to(to), nxt(nxt) {}
}edge[maxn*maxn];
int n, tot, head[maxn];
int low[maxn], deep[maxn], in[maxn*maxn], out[maxn*maxn], link[maxn*maxn];//low最早的深度 deep实际深度 in入度 out出度 所属强连通分量
int Linksum, dep;
stack<int> sta;
void init()
{
clr(edge, 0);
clr(low, 0);
clr(deep, 0);
clr(in, 0);
clr(out, 0);
clr(link,0);
while (!sta.empty()) { sta.pop(); }
tot = Linksum = dep = 0;
return;
}
void add(int a, int b)
{
edge[++tot].to = b;
edge[tot].nxt = head[a];
head[a] = tot;
return;
}
void Tarjan(int u)//求强连通分量一个点也代表一个
{
low[u] = deep[u] = ++dep;
sta.push(u);
int t;
for (int i = head[u]; i != 0; i = edge[i].nxt)
{
t = edge[i].to;
if (!deep[t])
{
Tarjan(t);
low[u] = min(low[u], low[t]);
}
else if (!link[t])
{
low[u] = min(low[u], deep[t]);
}
}
if (low[u] == deep[u])
{
++Linksum;
while (!sta.empty())
{
t = sta.top(); sta.pop();
link[t] = Linksum;
if (t == u) { break; }
}
}
return;
}
void solve()
{
for (int i = 1; i <= n; i++)
{
if (!deep[i]) { Tarjan(i); }
}
for(int k=1;k<=n;k++)//缩点
{
for (int i = head[k]; i != 0; i = edge[i].nxt)
{
int tm = edge[i].to;
if (link[tm] != link[k])
{
out[link[k]]++;
in[link[tm]]++;
}
}
}
int sumin = 0, sumout = 0;
for (int i = 1; i <= Linksum; ++i)
{
sumin += in[i] ? 0 : 1;
sumout += out[i] ? 0 : 1;
}
printf("%d\n%d\n", sumin, Linksum==1?0:max(sumout, sumin));
return;
}
int main()
{
int to;
while (~scanf("%d", &n))
{
init();
for (int i = 1; i <= n; ++i)
{
head[i]=0;
while (scanf("%d", &to) && to)
{
add(i, to);
}
}
solve();
}
return 0;
}