poj 1236 (强连通缩点入度问题)

题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

解题思路:1.先求强连通分支,然后把图缩小成为一个有向无环图;

2.求出这个有向无环图中,入度为0的点的个数为a,出度为0的点的个数为b;

3.第一个问题的答案就是a,第二个问题的答案为max{a,b};

4.最恶心的地方:当一开始这个图就是一个强连通图的时候,第二问题的答案为0。


#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=200 ;
const int M=10010 ;
struct node
{
	int u,v,next ;
}e1[M] ;
struct node1
{
	int u,v,next;
}e2[M] ;
int head1[N],head2[N],dfn[N],low[N],belong[N],stack[N],vist[N],in[N],out[N] ;
int  top1,top2,sum,cnt,dep;

void add1(int u , int v)
{
	  e1[top1].u=u;
	  e1[top1].v=v;
	  e1[top1].next=head1[u] ;
	  head1[u]=top1++;
}

void add2(int u , int v)
{
	  e2[top2].u=u;
	  e2[top2].v=v;
	  e2[top2].next=head2[u] ;
	  head2[u]=top2++;
}


void tarjan(int u)
{
	    low[u]=dfn[u]=++dep;
	    stack[cnt++]=u;
	    vist[u]=1;
	    for(int i = head1[u] ; i!= -1 ; i=e1[i].next)
	    {
	    	   int v=e1[i].v ;
	    	   if(!dfn[v])
	    	   {
	    	   	      tarjan(v) ;
	    	   	     low[u] = min( low[u] , low[v]) ;
	    	   }
	    	   else if(vist[v])
	    	       low[u] = min( low[u] , dfn[v] ) ;
	    }
	    if(dfn[u] == low[u])
	    {      int x;
	    	  sum++;
	    	  do
	    	  {
	    	  	    x = stack[--cnt];
	    	  	    vist[x]=0;
	    	  	    belong[x]=sum;
	    	  }while(x != u) ;
	    }
}

int main()
{
	 int  n; 
	 while(~scanf("%d",&n))
	 {
	        memset(head1,-1,sizeof(head1)); 
	        memset(head2,-1,sizeof(head2));
	        memset(belong,0,sizeof(belong)) ;
	        memset(in,0,sizeof(in));
	        memset(out,0,sizeof(out)) ;
	        top1=top2=0;
			for(int i = 1 ; i <= n ; i++)
			{
				 int to ;  
				scanf("%d",&to) ;
				while(to)
				{
					 add1(i,to) ;
					 scanf("%d",&to) ;
				}
			} 
			memset(vist,0,sizeof(vist));
			memset(low,0,sizeof(low));
			memset(dfn,0,sizeof(dfn));
			cnt=dep=sum=0; 
	        for(int i = 1 ; i <= n ; i++)
	           if(!dfn[i])
                      tarjan(i)	 ;
		    for(int i = 1 ; i <= n ; i++)
			   for(int j = head1[i] ; j!=-1 ; j=e1[j].next)
			     {   
			     	  int v = e1[j].v ;
			     	  if(belong[i] != belong[v])
					  { // printf("**");
					    add2(belong[i],belong[v]) ; 
					  }
			     }			   
			     
		   	for(int i = 1 ; i <= sum ; i++)
			   for(int j = head2[i] ; j!=-1 ; j=e2[j].next)
			     {
			     	  int v = e2[j].v ;
			     	  in[v]++;
			     	  out[i]++;
			     } 
		  int a=0,b=0;		  //   printf("%d ",sum );
		  for(int i = 1 ; i <= sum ; i++)
		   {
		   	    if(in[i] == 0 )
		   	        a++ ;
		        if(out[i] == 0 )
				    b++ ;	
		   }	  
		  if(sum <= 1 )
		  {
		  	    printf("1\n");
		  	    printf("0\n") ;
		  } 
		  else
		  {
		  	     printf("%d\n",a);
		  	     printf("%d\n",max(a,b)) ;
		  }
		      
	 }  
	return 0 ;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值