描述
一些学校的校园网连接在一个计算机网络上。学校之间存在软件支援协议。每个学校都有它应支援的学校名单(学校a支援学校b,并不表示学校b一定支援学校a)。当某校获得一个新软件时,无论是直接得到的还是从网络得到的,该校都应立即将这个软件通过网络传送给它应支援的学校。因此,若需要让所有连接在网络上的学校都能使用一个新软件,只需要将其提供给其中一些学校即可。
子任务a:根据学校间软件支援协议(各个学校的支援名单),计算最少需要将一个软件直接提供给多少个学校,才能使该软件通过网络传送到所有学校。
子任务b:如果允许在原有支援协议上添加新的支援关系,则总可以形成一个新的协议,使得此时只需要将一个新软件提供给任何一个学校,其他所有学校就都可以通过网络获得该软件。请计算出最少需要添加几条新的支援关系。
输入格式
第一行是一个整数 n(2≤n≤100),表示与网络连接的学校总数。接下来 n行描述了每个学校要支援的学校。第i+1行表示第i 号学校要支援的所有学校的编号,编号之间用空格隔开,每行以数字0 结束。如果某个学校不支援任何学校,则相应的行会有一个0。
输入:
5
2 4 3 0
4 5 0
0
0
1 0
输出
1
2
定理:使图变为双连通的所添加的无向边数目为:(缩点后的图的叶子结点数+1)/2;
使有向图变为强连通的所添加的有向边数目为:缩点后 max(入度为0的结点数目,出度为0的结点数目);
思路:任务a即哪些点开始遍历可将图中的结点遍历完毕,求这些点的最小数目。任务b即最少添加多少条边可使有向图变为一个强连通图。将有向图缩点后任务a即为入度为0的连通分量,任务b为入度为0的连通分量数目与出度为0的连通分量数目较大的一个,若只有一个连通分量直接输出1,0。
PS:当结点数目较少时用邻接矩阵表示图,防止存在重边。
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int MAXN=105; int mp[MAXN][MAXN]; int n; int dfn[MAXN],low[MAXN],index; int stack[MAXN],top; bool ins[MAXN]; int belong[MAXN],cnt; void tarjan(int u) { dfn[u]=low[u]=++index; stack[top++]=u; ins[u]=true; for(int v=1;v<=n;v++) { if(mp[u][v]) { if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(ins[v]) low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]) { int v; ++cnt; do{ v=stack[--top]; ins[v]=false; belong[v]=cnt; }while(u!=v); } } int indeg[MAXN]; int outdeg[MAXN]; void solve() { for(int i=1;i<=n;i++) { for(int v=1;v<=n;v++) { if(mp[i][v]) { if(belong[i]!=belong[v]) { indeg[belong[v]]++; outdeg[belong[i]]++; } } } } int in=0,out=0; for(int i=1;i<=cnt;i++) { if(indeg[i]==0) in++; if(outdeg[i]==0) out++; } if(cnt!=1) printf("%d %d ",in,max(in,out)); else printf("%d %d ",1,0); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int v; do{ scanf("%d",&v); if(v==0) break; else mp[i][v]=1; }while(true); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); solve(); return 0; }