POJ 1236 Network of Schools (强连通 出度 入度)

题目:N(2=<N<=100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输
问题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;  
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值