题意:网络中的一学校可以将软件发送给其他一些学校,能够发送给谁取决于他们各自维护的一个清单。将学校看成一个节点,给出每个学校的维护清单,问至少需要复制几次软件,使毎个学校都能够得到该软件,在清单中至少添加几项,可使软件至少复制一次,所有学校都可以得到。
思路:
1、用Tarjan算法求出强连通分分量。2、缩点重新构图。3、分别求节点的出度和入度。
第一个问题就是入度为零的个数,第二问题就是出度和入度中的较大者。
#include<iostream> #include<memory.h> #include<stdio.h> using namespace std; struct edge { int t; edge *next; }*V[101],*s; int DFN[101],LOW[101];//DFN[u]为节点u搜索的次序编号, //low[u]为节点u的子树能够追溯到的最早的栈中节点的次序号 int Stop,Bcnt,Dindex; bool instack[101]; int Stap[101]; int n,m; int Belong[101]; int in[101];//入度 int out[101];//出度 void insert(int u,int v,edge *k[]) { edge *temp = new edge; temp -> t = v; temp -> next = k[u]; k[u] = temp; } void tarjan(int i) { int j; DFN[i] = LOW[i] = ++Dindex; Stap[++Stop] = i; instack[i] = true; for(edge *e = V[i]; e ; e = e->next) { j = e->t; if(!DFN[j]) { tarjan(j); if(LOW[i] > LOW[j]) LOW[i] = LOW[j]; } else if(instack[j]&&DFN[j] < LOW[i]) LOW[i] = DFN[j]; } if(DFN[i] == LOW[i]) { ++Bcnt; do { j = Stap[Stop--]; instack[j] = false; Belong[j] = Bcnt; } while(j != i); } } void find() { Stop = Bcnt = Dindex; memset(DFN , 0 ,sizeof(DFN)); for(int i = 0 ; i < n ; ++i) { if(!DFN[i]) tarjan(i); } } int main() { cin>>n; for(int i = 0 ; i < n ; ++i) { while(1) { int a; if(scanf("%d",&a)&&!a) break; insert(i , a - 1 , V); } } find(); memset(in , 0 , sizeof(in)); memset(out, 0 , sizeof(out)); int sum = 0; int sum1= 0; for(int i = 0 ; i < n ; ++i) { s = V[i]; while(s) { int j = s ->t; if(Belong[j] != Belong[i]) { out[Belong[i]]++; in[Belong[j]]++; } s = s->next; } } for(int i = 1; i <= Bcnt; ++i) { if(in[i] == 0) sum++; if(out[i] == 0) sum1++; } if(Bcnt == 1) sum1 = 0; else if(sum > sum1) sum1 = sum; printf("%d\n%d\n",sum,sum1); return 0; }