题目:N(2=<N<=100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输
问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。
2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
思路:先求强连通分量:1、入度为0的点的个数即为第一个答案;
2、第二问也就是在DAG图中,添加多少条边使其成为强连通图
入度和出度为0的个数的较大值为第二个答案(强连通分量个数为1时,第二个答案为0)。
问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。
2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
思路:先求强连通分量:1、入度为0的点的个数即为第一个答案;
2、第二问也就是在DAG图中,添加多少条边使其成为强连通图
入度和出度为0的个数的较大值为第二个答案(强连通分量个数为1时,第二个答案为0)。
也就是由出度为0的向入度为0的连边。
#include <cstdio>
#include <cstring>
#include <stack>
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
const int INF=0x3f3f3f3f;
const int nPoint=1010; //原节点数
const int nEdges=30005; //原边数
const int nNewmap=1005; //新图最大节点数
class SCC // strongly connected components
{//节点标号从1开始
private:
struct Edge
{
int v,next;
}edges[nEdges];
int dfn[nPoint],low[nPoint],head[nPoint];//dfn(u)为节点u搜索的次序编号(时间戳),low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号
bool visit[nPoint]; //标记是否在栈中
int in[nPoint],out[nPoint],color[nPoint]; //color[]保存各强连通分量包含的节点数,in[]各强连通分量的入度,out[]各强连通分量的出度
int belong[nPoint]; //每个结点所对应的强连通分量标号数组
bool DAG[nNewmap][nNewmap]; //存储缩点之后的新图,有向无环图DAG
int n,e,id,colornum; //colornum强连通分量的个数
int nIn_0,nOut_0; //nIn_0=0入度为0的点的个数,nOut_0=0出度为0的点个数
stack<int> S;
int cnt[nPoint]; //存储割点被切掉后会新分出的块数,可用来统计割点个数(不为0即为割点)
void DFS (int u)
{
int i,top,v;
dfn[u]=low[u]=++id; //id为时间戳
S.push(u);
visit[u]=true;
for (i=head[u];i!=-1;i=edges[i].next)
{
v=edges[i].v;
if (dfn[v]==0)
{//未被访问
DFS(v);//继续向下找
if (low[v]>=dfn[u]) //是割点,统计
cnt[u]++;
low[u]=min(low[u],low[v]);//更新u节点所能到达的最小层数
}
else if (visit[v])
low[u]=min(low[u],dfn[v]);
}
if (dfn[u]<=low[u]) // ==
{//如果节点u是强连通分量的根
colornum++; //连通分量标号+1
do
{
top=S.top(); //
S.pop();
visit[top]=false;
belong[top]=colornum; //出栈节点top属于colornum标号的强连通分量
color[colornum]++;
}while (top!=u); //直接将u从栈中退出
}
}
public:
void Init (int _n)
{
n=_n;
id=colornum=e=0;
nIn_0=nOut_0=0;
memset(head,-1,sizeof(head));
memset(visit,false,sizeof(visit));
memset(dfn,0,sizeof(dfn));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(cnt,0,sizeof(cnt));
}
void Add (int u,int v)
{
edges[e].v=v;
edges[e].next=head[u];
head[u]=e++;
}
int Tarjan () //返回强连通分量的个数
{
while (!S.empty()) //清空栈
S.pop();
for (int i=1;i<=n;i++) //枚举每个节点,搜索连通分量
if (!dfn[i]) //未被访问
{
DFS(i); //则找该节点的连通分量
cnt[i]--; //原来本身有一块
}
return colornum;
}
void Cal () //计算in[],out[],nOut_0,nIn_0
{//与TopoOrder ()同时调则无法正确计算 in 数组(重复使用)
int i,j;
for (i=1;i<=n;i++)
for (j=head[i];j!=-1;j=edges[j].next)
if (belong[i]!=belong[edges[j].v])
{
in[belong[edges[j].v]]++;
out[belong[i]]++;
}
for (i=1;i<=colornum;i++)
{
nOut_0+=!out[i];
nIn_0+=!in[i];
}
printf("%d\n",nIn_0);
printf("%d\n",max(nIn_0,nOut_0));
}
void Build () //建立新图DAG
{
memset(DAG,0,sizeof(DAG));
for (int i=1;i<=n;i++)
for (int j=head[i];j!=-1;j=edges[j].next)
if (belong[i]!=belong[edges[j].v])
DAG[belong[i]] [belong[edges[j].v]] =true;
}
bool TopoOrder () //拓扑排序,返回是否有分叉
{//既是否为一条链
int i,j;
for (i=1;i<=colornum;i++)
for (j=1;j<=colornum;j++)
if (DAG[i][j]) in[j]++;
for (i=1;i<colornum;i++)
{
int cnt=0; //分支条数
int p=0; //下一节点
for (j=1;j<=colornum && cnt<=1;j++)
if (in[j]==0)
cnt++,p=j;
if (cnt>1) return false;
for (j=1;j<=colornum;j++)
if (DAG[p][j]) in[j]--;
in[p]=INF;
}
return true;
}
void Deal ()
{//计算割点数目
Tarjan ();
int k=0;
for (int i=1;i<=n;i++)
if (cnt[i])
k++;
printf("%d\n",k);
}
}ob;
int main ()
{
#ifdef ONLINE_JUDGE
#else
freopen("read.txt","r",stdin);
#endif
int n,u,v;
scanf("%d",&n);
ob.Init (n);
for (u=1;u<=n;u++) while(scanf("%d",&v)&&v)
ob.Add(u,v);
if (ob.Tarjan()==1)
printf("1\n0\n");
else
ob.Cal();
return 0;
}