POJ 1236 tarjan+缩点
题意:
N(2<N<100) 各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校 最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
题解:
看网上一位大牛说的很好,
第一个答案是入度为0的强连通块数。
因为首先,入度为0的强连通块因为没有人可以传给他信息,所以一定是答案。而其他点因为有入度所以有强通块可以传给。
而如果这个父亲没有入度,那么一定在之前已经给过他东西了;如果有入度,那么就可以以此类推到爷爷,一定有答案。
第二个答案是max(入读为0的点,出度为0的点)。
因为如果两个一样大,正好两两配对。如果不一样大,那么一样个数的点可以两两配对;而多出来的点一定要给它加边让他连上普通点。
一个点的时候特判,其余没了。
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <iostream> #include <vector> #include <cmath> #include <map> #include <string> #include <stack> #include <queue> using namespace std; const int MAXN=1e5+10; struct node { int v; int nxt; }; node edge[MAXN];//边数 int ans1=0,ans2=0; int head[MAXN]; int n,m; int Stop,Bcnt,Dindex;//栈头,强通块数,时间戳 int DFN[MAXN],LOW[MAXN];//首时间戳,最近回溯点(根) int Stap[MAXN];//答案栈 int instack[MAXN];//是否在栈中 int Belong[MAXN];//这个点属于第几个强连通块(点) int cnt=0; void add_edge(int u,int v) { edge[cnt].v=v; edge[cnt].nxt=head[u]; head[u]=cnt; cnt++; } void tarjan(int i) { int j; DFN[i]=LOW[i]=++Dindex; instack[i]=1; Stap[++Stop]=i; for (int e=head[i]; e!=-1; e=edge[e].nxt) { j=edge[e].v; if (!DFN[j])//儿子没遍历 { tarjan(j);//遍历 if (LOW[j]<LOW[i])//如果儿子已经形成环 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]=0;//标记 Belong[j]=Bcnt;//标记 } while (j!=i);//亦可做LOW[j]!=DFN[j],如果到根,两个条件同时成立 } } void solve() { int i; Stop=Bcnt=Dindex=0;//栈头,强通块数,时间戳 memset(DFN,0,sizeof(DFN)); memset(LOW,0,sizeof(LOW)); for (int i=1; i<=n; i++) if (!DFN[i]) tarjan(i); int in[MAXN],out[MAXN]; memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); for (int i=1;i<=n;i++) { for (int j=head[i];j!=-1;j=edge[j].nxt) { if (Belong[i]!=Belong[edge[j].v]) { ++out[Belong[i]]; ++in[Belong[edge[j].v]]; } } } int A=0,B=0; for (int i=1;i<=Bcnt;i++) { if (!in[i]) A++; if (!out[i]) B++; } // printf ("A==%d B==%d\n",A,B); if (Bcnt==1) printf ("1\n0\n"); else printf ("%d\n%d\n",A,max(A,B)); } int main() { while (scanf ("%d",&n)!=EOF) { cnt=0; memset(head,-1,sizeof(head)); for (int i=1;i<=n;i++) { int t1; while (scanf ("%d",&t1)&&t1) { add_edge(i,t1); } } solve(); } return 0; }