1. 程式人生 > >[tarjan縮點] 洛谷P2746 [USACO5.3]校園網Network of Schools

[tarjan縮點] 洛谷P2746 [USACO5.3]校園網Network of Schools

審視 print blog tarjan urn printf 轉換 scanf 不難

一開始完全沒有搞懂題目的意思就下手,但是居然還AC了兩個點?

仔細審視了一下題目的意思,發現題目並不難。

對於第一問,我們只需要求縮點後,入度為 0 的點的數量就可以了。

對於第二問,我們的目標是要求縮點後的所有點互相聯通(因為只有這樣,任選一個點才能互相到達)
我們轉換一下含義:縮點後的所有點只有入度和出度都大於0 才為互相聯通
所以第二問我們只需要對入度為0的點和出度0的點做個比較,誰大取誰的值輸出。

坑點:需要特判一下只有一個聯通塊的時候,否則會出錯。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include 
<stack> using std::stack; const int N = 233; struct node{ int u,v,next; node(){} node(int _u,int _v,int _next){ u = _u; v = _v; next = _next; } }Edge[N*N]; int head[N*N],Count; int dfn[N],low[N]; //scc_cnt是聯通塊的個數(縮點後的點的數量 //sccno是縮點後的序號
int scc_cnt,step,sccno[N]; int n; void AddEdge(int u,int v){ Count++; Edge[Count] = node(u,v,head[u]); head[u] = Count; } void init(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x; while(scanf("%d",&x) && x!=0){ AddEdge(i,x); } } } stack
<int>S; void dfs(int x){ dfn[x] = low[x] = ++step; S.push(x); for(int i = head[x];i;i=Edge[i].next){ int v = Edge[i].v; if(!dfn[v]){ dfs(v); low[x] = std::min(low[x],low[v]); } else if(!sccno[v]) low[x] =std::min(low[x],dfn[v]); } if(low[x] == dfn[x]){ scc_cnt++; while(1){ int u = S.top(); S.pop(); sccno[u] = scc_cnt; if(x==u) break; } } } int To[N],Out[N]; int main(){ init(); for(int i=1;i<=n;i++){ if(!dfn[i]) dfs(i); } for(int i=1;i<=Count;i++){ if(sccno[Edge[i].u]!=sccno[Edge[i].v]){ Out[sccno[Edge[i].u]]++; To[sccno[Edge[i].v]]++; } } //To入度 Out出度 int ask1=0,ask2=0; for(int i=1;i<=scc_cnt;i++){ if( !To[i] ) ask1++; if( !Out[i] ) ask2++; } int End = std::max(ask1,ask2); if(scc_cnt==1){ printf("1\n0\n"); } else{ printf("%d\n%d\n",ask1,End); } return 0; }

[tarjan縮點] 洛谷P2746 [USACO5.3]校園網Network of Schools