1. 程式人生 > >poj1236 Network of Schools(taejan縮點)

poj1236 Network of Schools(taejan縮點)

題目連結:http://poj.org/problem?id=1236

題意:有n個學校,學校之間的網路由單向邊連線,現在有一個軟體要傳向每個學校,(單向連通的可以直接傳達)

問最少要傳給多少個學校可以全部傳達?

再至少新增幾條單向邊,可以隨便傳送給一個學校使全部學校傳達到。

 

思路:

先找到連通分量,每個連通分量入度為0的就是要傳達的,第一問就是入度為0的個數


第二問就是新增幾條邊可以得到使整個圖變成一個強連通分量,答案就是連通分量入度為0的個數和出度為0的個數中最大的那個值
原因:我們將縮完點之後的圖看成一個個子樹,要想把它們變成一個連通分量就要將出度為0和入度為0的點連起來。
所以只要只要找兩者最大的就行。

為什麼縮點後再計算出度入度呢?

因為不縮點的話,可能有環,以一個一個點為單位用出度是算不出答案來,只有以一個一個連通分量為單位,計算出度入度。

 

程式碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=105;
struct node{
    
int to,next; }edge[maxn*maxn]; int head[maxn],low[maxn],dfn[maxn],st[maxn];//st模擬棧 int in[maxn],out[maxn],visit[maxn],belong[maxn];//belong每個點屬於哪個連通分量 int n,num,cnt,tot,top,ans1,ans2; void init()//初始化 { memset(head,-1,sizeof(head)); memset(visit,0,sizeof(visit)); memset(dfn,0,sizeof
(dfn)); memset(low,0,sizeof(low)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(st,0,sizeof(st)); memset(belong,0,sizeof(belong)); ans1=ans2=cnt=tot=num=top=0; } void add(int u,int v)//前向星加邊 { edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } void tarjan(int u)//tarjan縮點 { dfn[u]=low[u]=++tot;//賦初值 visit[u]=1;//標記進棧 st[++top]=u;//進棧 for(int i=head[u];i!=-1;i=edge[i].next)//搜尋相連的邊 { int v=edge[i].to; if(!dfn[v])//沒有被搜尋過 { tarjan(v);//搜尋 low[u]=min(low[u],low[v]); } else if(visit[v])//搜尋過,已經在棧裡 low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u])//一個連通分量 { int t; num++;//聯通分量的編號 do{ t=st[top--];//出棧 visit[t]=0;//取消標記 belong[t]=num;//記錄所在連通分量 }while(t!=u); } } void solve() { 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!=-1;j=edge[j].next) { int v=edge[j].to; if(belong[i]!=belong[v])//不是同一個縮點 { out[belong[i]]++; in[belong[v]]++; } } } for(int i=1;i<=num;i++) { if(!in[i]) ans1++; if(!out[i]) ans2++; } } int main() { while(scanf("%d",&n)!=EOF) { init(); int x; for(int i=1;i<=n;i++) { while(scanf("%d",&x) && x!=0) add(i,x); } solve(); ans2=max(ans1,ans2); if(num==1) printf("1\n0\n"); else printf("%d\n%d\n",ans1,ans2); } return 0; }