題目大意
有N個學校,這些學校之間用一些單向邊連接,若學校A連接到學校B(B不一定連接到A),那麼給學校A發一套軟件,則學校B也可以獲得。現給出學校之間的連接關係,求出至少給幾個學校分發軟件,才能使得所有的學校均可以獲得軟件;以及,至少需要添加幾條單向邊連接學校,才能使得給這些學校中任何一所發軟件,其餘的學校均可以收到。
第一是需要給多少個點,才能傳遍所有點。
第二問是加多少條邊,使得整個圖變得強連通。
使用Tarjan進行縮點,得到一個SCC圖、
這個圖有多少個入度爲0的,多少個出度爲0的。
假設有n個入度爲0,m個出度爲0
那麼第一個答案就是n,第二個答案是max(n,m)
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 110;
const int MAXM = 110*110;
struct Edge
{
int to,next;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
int scc;//強聯通數
bool Instack[MAXN];
void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;
}
void Tarjan(int u)
{
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
for(int i = head[u];i != -1;i = edge[i].next)
{
v = edge[i].to;
if(!DFN[v]) // 如果新搜索到的節點是從未被搜索過
{
Tarjan(v); // 那自然就得搜索這個節點
if(Low[u] > Low[v])// 回溯的時候改變當前節點的low值
Low[u] = Low[v];
}
else if(Instack[v] && Low[u] > DFN[v])// 如果新搜索到的節點已經被搜索過而且現在在棧中 ,更新當前節點的low值,這裏的意思是兩個節點之間有一條可達邊,而前面 節點已經在棧中,那麼後面的節點就可能和前面的節點在一個聯通分量中
Low[u] = DFN[v];
}
if(Low[u] == DFN[u])
{
scc++;
do
{
v = Stack[--top];// 彈出棧元素
Belong[v] = scc;// 爲了方便計算,將強聯通分量進行標記
Instack[v] = false; // 將棧內標記置爲0
}
while( v!= u); // 一直彈到u出現爲止
}
}
int in[MAXN],out[MAXN];
void solve(int N)
{
memset(DFN,0,sizeof(DFN));
memset(Instack,false,sizeof(Instack));
Index = scc = top = 0;
for(int i = 1;i <= N;i++)
if(!DFN[i])// 如果某點沒被訪問過,則對其進行tarjan
Tarjan(i); // tarjan的成果是得到了一個belong數組,記錄每個節點分別屬於哪個強聯通分量
if(scc == 1)
{
printf("1\n0\n");
return;
}
for(int i = 1;i <= scc;i++)
in[i] = out[i] = 0;
//將強連通分支縮成點,然後統計每個強連通分支的入度和出度
for(int u = 1;u <= N;u++)
{
for(int i = head[u];i != -1;i = edge[i].next)
{
int v = edge[i].to;
if(Belong[u] != Belong[v]) //注意!!!自身不能連接到自身!
{
in[Belong[v]]++;
out[Belong[u]]++;
}
}
}
int ans1=0,ans2=0;
for(int i = 1;i <= scc;i++)
{
if(in[i]==0)ans1++;
if(out[i]==0)ans2++;
}
printf("%d\n%d\n",ans1,max(ans1,ans2));
}
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}
int main()
{
int n;
int v;
while(scanf("%d",&n) == 1)
{
init();
for(int i = 1;i <= n;i++)
{
while(scanf("%d",&v)==1 && v)
{
addedge(i,v);
}
}
solve(n);
}
return 0;
}
//#define LOCAL
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
#define MAXN 110
#define INF 0x3f3f3f3f
int n;
int map[MAXN][MAXN];
int low[MAXN];
int dfn[MAXN];
int stack[MAXN], head;
int instack[MAXN];
int belong[MAXN];
int in[MAXN];
int out[MAXN];
int index, cnt;
int min(int a, int b)
{
return a < b ? a : b;
}
int max(int a, int b)
{
return a > b ? a : b;
}
void init()
{
int i, j;
int temp;
memset(map, 0, sizeof(map));
memset(dfn, -1, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(instack, 0, sizeof(instack));
index = cnt = 1;
head = 0;
for(i= 1; i <= n; i++)
{
while(scanf("%d", &temp) && temp)
{
map[i][temp] = 1;
}
}
}
void tarjan(int x)
{
int i, a;
low[x] = dfn[x] = index; // 剛搜到一個節點時low = dfn
index++;
stack[++head] = x; // 將該節點入棧
instack[x] = 1; // 將入棧標記設置爲1
for(i = 1; i <= n; i++) // 遍歷入棧節點的邊
{
if(!map[x][i]) // 如果兩點之間沒有邊
continue; // 不用管它
if(dfn[i] == -1) // 如果新搜索到的節點是從未被搜索過
{
tarjan(i); // 那自然就得搜索這個節點
low[x] = min(low[x], low[i]); // 回溯的時候改變當前節點的low值
}
else if(instack[i]) // 如果新搜索到的節點已經被搜索過而且現在在棧中
{
low[x] = min(low[x], dfn[i]); // 更新當前節點的low值,這裏的意思是兩個節點之間有一條可達邊,而前面
} // 而前面節點已經在棧中,那麼後面的節點就可能和前面的節點在一個聯通分量中
}
if(low[x] == dfn[x]) // 最終退回來的時候 low == dfn , 沒有節點能將根節點更新,那
{ // low == dfn 的節點必然就是根節點
int temp;
while(1) // 一直出棧到此節點, 這些元素是一個強聯通分量
{
temp = stack[head--]; // 彈出棧元素
belong[temp] = cnt; // 爲了方便計算,將強聯通分量進行標記
instack[temp] = 0; // 將棧內標記置爲0
if(temp == x) // 一直彈到x出現爲止
break;
}
cnt++;
}
}
void solve()
{
int i, j;
int t1, t2;
while(scanf("%d", &n) != EOF) //
{
init(); // 初始化
for(i = 1; i <= n; i++) //
if(dfn[i] == -1) // 如果某點沒被訪問過,則對其進行tarjan
tarjan(i); // tarjan的成果是得到了一個belong數組,記錄每個節點分別屬於哪個強聯通分量
for(i = 1; i <= n; i++) // 遍歷每條邊,找到縮點之後的邊
{
for(j = 1;j <= n; j++)
{
if(map[i][j] && belong[i] != belong[j]) // 兩點之間有邊,但不是屬於一個強聯通分量的邊
{
out[belong[i]]++; // 縮點後的點入度+1
in[belong[j]]++;// 縮點後的點出度+1
}
}
}
t1 = 0, t2 = 0;
for(i = 1; i < cnt; i++)
{
if(in[i] == 0)
t1++;
if(out[i] == 0)
t2++;
}
if(cnt == 2)
printf("1\n0\n");
else
printf("%d\n%d\n", t1, max(t1, t2));
}
}
int main()
{
#ifdef LOCAL
freopen("poj1236.txt", "r", stdin);
// freopen(".txt", "w", stdout);
#endif
solve();
return 0;
}