洛谷 P2746 [USACO5.3]校園網Network of Schools 解題報告
阿新 • • 發佈:2018-06-10
include print 正整數 討論 math cst code 格式 一個
P2746 [USACO5.3]校園網Network of Schools
題目描述
一些學校連入一個電腦網絡。那些學校已訂立了協議:每個學校都會給其它的一些學校分發軟件(稱作“接受學校”)。註意即使 B 在 A 學校的分發列表中, A 也不一定在 B 學校的列表中。
你要寫一個程序計算,根據協議,為了讓網絡中所有的學校都用上新軟件,必須接受新軟件副本的最少學校數目(子任務 A)。更進一步,我們想要確定通過給任意一個學校發送新軟件,這個軟件就會分發到網絡中的所有學校。為了完成這個任務,我們可能必須擴展接收學校列表,使其加入新成員。計算最少需要增加幾個擴展,使得不論我們給哪個學校發送新軟件,它都會到達其余所有的學校(子任務 B)。一個擴展就是在一個學校的接收學校列表中引入一個新成員。
輸入輸出格式
輸入格式:
輸入文件的第一行包括一個整數 N:網絡中的學校數目\((2<=N<=100)\)。學校用前 N 個正整數標識。
接下來 N 行中每行都表示一個接收學校列表(分發列表)。第 i+1 行包括學校 i 的接收學校的標識符。每個列表用 0 結束。空列表只用一個 0 表示。
輸出格式:
你的程序應該在輸出文件中輸出兩行。
第一行應該包括一個正整數:子任務 A 的解。
第二行應該包括子任務 B 的解。
第一步顯然要縮點。。。
第二步統計新點每個點的入度和初度。
第一問顯然就是有多少點入度為0,把機子放到那些點就行了。第二問我們把入度和初度為0的點連起來造環,取數量大的那個。
值得一提的是,縮完了以後是單點的情況要額外討論喵
code:
#include <cstdio> int min(int x,int y){return x<y?x:y;} int max(int x,int y){return x>y?x:y;} const int N=101; int n; struct Edge { int to,next; }edge[N*N]; int head[N],cnt=0; void add(int u,int v) { edge[++cnt].next=head[u];edge[cnt].to=v;head[u]=cnt; } int low[N],dfn[N],is[N],in[N],out[N],s[N],ha[N],tot=0,time=0,n0=0; void push(int x){s[++tot]=x;} void pop(){tot--;} void tarjan(int now) { low[now]=dfn[now]=++time; is[now]=1; push(now); for(int i=head[now];i;i=edge[i].next) { int v=edge[i].to; if(!dfn[v]) { tarjan(v); low[now]=min(low[now],low[v]); } else if(is[v]) low[now]=min(low[now],dfn[v]); } if(low[now]==dfn[now]) { n0++;int k; do { k=s[tot]; pop(); is[k]=0; ha[k]=n0; }while(k!=now); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int v; scanf("%d",&v); while(v) { add(i,v); scanf("%d",&v); } } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].next) { int v=edge[j].to; if(ha[v]!=ha[i]) in[ha[v]]++,out[ha[i]]++; } int ans1=0,ans2=0; if(n0==1) {printf("1\n0\n");return 0;} for(int i=1;i<=n0;i++) { if(!in[i]) ans1++; if(!out[i]) ans2++; } printf("%d\n%d\n",ans1,max(ans2,ans1)); return 0; }
2018.6.10
洛谷 P2746 [USACO5.3]校園網Network of Schools 解題報告