一些學校連接到了一個計算機網絡。網絡中的學校間有如下約定:每個學校維護一個列表,當該學校收到軟件或信息後將會轉發給列表中的所有學校(也就是接收方列表)。需要注意的是如果B學校在A學校的接收方列表中,A學校不一定會出現在B學校的接收方列表中。
你現在的任務是寫出一個程序來計算必須收到一份軟件來使網絡中的所有學校都能收到軟件的學校的數量的最小值(此爲子任務A)。作爲一個遠期目標,我們希望給任意一個學校發送一份軟件都能使網絡中的所有學校都收到軟件。爲了實現這個目標,我們或許需要在一些學校的接收方列表中添加新項。 你現在需要計算出至少需要添加多少新項才能實現這個遠期目標(此爲子任務B)。
Input
第一行是一個整數N:計算機網絡中的學校數量(2<=N<=100)。接下來有N行數據,接下來的第 i 行描述了第 i 個學校的接收方列表。每行列表以0結尾。如果第 j 行中只有一個0代表第 j 個學校的接收方列表是空的。
Output
你的程序應該在標準輸出中輸出兩行。每一行應爲一個整數:第一行爲子任務A的解,第二行爲子任務B的解。
Sample Input
5 2 4 3 0 4 5 0 0 0 1 0 |
---|
Sample Output
1 2 |
---|
首先縮點,查看有幾個強連通圖,查看縮點後的DAG(有向無環圖)入度爲0和出度爲0的多少,選取其中最大值。
#include <cstdio>
#include <stack>
#include <cstring>
using namespace std;
const int MAXN=1001;
struct node{
int v,next;
}edge[MAXN*10];
int dfn[MAXN],low[MAXN];
int head[MAXN],cnt,belong[MAXN];
bool vis[MAXN];
int tim;
stack<int >st;
int out[MAXN],in[MAXN];
int num=0;
void tarjan(int u)
{
dfn[u]=low[u]=++tim;
vis[u]= true;
st.push(u);
for (int i = head[u]; i !=-1 ; i=edge[i].next) {
if(!dfn[edge[i].v]) {
tarjan(edge[i].v);
low[u]=min(low[u],low[edge[i].v]);
}
else if(vis[edge[i].v]){
low[u]=min(low[u],dfn[edge[i].v]);
}
}
if(dfn[u]==low[u])
{
int x;
++num;
while(1)
{
x=st.top();
st.pop();
vis[x]=0;
belong[x]=num;
if(x==u)break;
}
}
}
void add(int x,int y)
{
edge[++cnt].next=head[x];
edge[cnt].v=y;
head[x]=cnt;
return ;
}
void slove(int n)
{
for (int i = 1; i <=n ; ++i) {
if(dfn[i]==0) tarjan(i);
}
for (int j = 1; j <=n ; ++j) {
for (int k = head[j]; k !=-1 ; k=edge[k].next) {
int v=edge[k].v;
if(belong[j]!=belong[v]) {
out[belong[j]]++;
in[belong[v]]++;
}
}
}
int ans1=0,ans2=0;
for (int i = 1; i <=num ; ++i) {
if(in[i]==0)ans1++;
if(out[i]==0) ans2++;
}
ans2=max(ans1,ans2);
if(num==1)printf("1\n0\n");
else
printf("%d\n%d\n",ans1,ans2);
}
int main()
{
int n;
scanf("%d",&n);
int x;
cnt=0;
memset(head,-1, sizeof(head));
for (int i = 1; i <=n ; ++i) {
while (scanf("%d",&x)&&x) {
add(i,x);
}
}
slove(n);
return 0;
}