题目链接:
http://poj.org/problem?id=1236
题目大意:
N台电脑之间能够通过有向边(u,v)从第u台电脑传输文件到第v台电脑。如果给第u台电脑投放
一个文件,那么这个文件就能通过有向边传输到第v台电脑上,给你N台电脑的连接情况。
那么问题来了:1、最少向这N台电脑中的几台电脑投放文件,就能使N台电脑都能接收到文件。
2、最少向这N台电脑构成的图中添加几条边,使只向一台电脑投放文件,就能够是N台电脑都
能接收到文件。
思路:
该图中的文件具有传递性。很快发现强连通的特征。对应图中的一个强连通分量,只要向其中的
一个点投放文件,那么这个强连通分量就都能收到文件。将这个强连通分量缩点变为DAG(有向
无环图)。这是解第一个问题的基础。
在有向无环图中,边变为了强连通分量之间的文件传输关系。意味这:只要一个强连通分量有入
边,那么就可以通过这个入边从另外一个分量中接收文件。但是,无环图意味着肯定存在没有入
度(入度为0)的强连通分量,这些强连通分量没有文件来源,所以要作为投放文件的位置。那么,
第一问就只需要计算出缩点后入度为0的强连通分量数目即可。
而第二个问题,把一个有向无环图转换为一个强连通分量。强连通分量的主要特征是:每个点的
入度和出度都不为0,那么计算出入度为0的点的个数SumIn和出度为0的点的个数SumOut,题
目就变为了:在入度为0的点和出出度为0的点之间最少加多少边。很明显的可以看出,答案就是
max(SumIn,SumOut)。
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int MAXN = 110; const int MAXM = 10100; struct EdgeNode { int to; int next; }Edges[MAXM]; int Head[MAXN],vis[MAXN],low[MAXN]; int dfn[MAXN],Stack[MAXN],indegree[MAXN],outdegree[MAXN]; int Count[MAXN],m,id; void AddEdges(int u,int v) { Edges[id].to = v; Edges[id].next = Head[u]; Head[u] = id++; } int TarBFS(int pos,int lay,int &scc) { vis[pos] = 1; low[pos] = dfn[pos] = lay; Stack[++m] = pos; for(int i = Head[pos]; i != -1; i = Edges[i].next) { if(!vis[Edges[i].to]) TarBFS(Edges[i].to,++lay,scc); if(vis[Edges[i].to] == 1) low[pos] = min(low[pos],low[Edges[i].to]); } if(dfn[pos] == low[pos]) { ++scc; do { Count[scc]++; low[Stack[m]] = scc; vis[Stack[m]] = 2; }while(Stack[m--] != pos); } return 0; } void Tarjan(int N) { int scc, temp, lay; scc = temp = m = 0; lay = 1; memset(vis,0,sizeof(vis)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); for(int i = 1; i <= N; ++i) if(vis[i] == 0) TarBFS(i,lay,scc); for(int i = 1; i <= N; ++i) { for(int j = Head[i]; j != -1; j = Edges[j].next) if(low[i] != low[Edges[j].to]) { outdegree[low[i]]++; indegree[low[Edges[j].to]]++; } } int SumIn = 0, SumOut = 0; for(int i = 1; i <= scc; ++i) { if(!indegree[i]) SumIn++; if(!outdegree[i]) SumOut++; } if(scc == 1) printf("1\n0\n"); else printf("%d\n%d\n", SumIn, max(SumIn,SumOut)); } int main() { int N, v; while(~scanf("%d", &N)) { memset(Head,-1,sizeof(Head)); memset(outdegree,0,sizeof(outdegree)); memset(indegree,0,sizeof(indegree)); memset(Count,0,sizeof(Count)); id = 0; for(int i = 1; i <= N; ++i) while(scanf("%d",&v) && v) AddEdges(i,v); Tarjan(N); } return 0; }