这题是广度优先搜索的延伸,需要额外记录层数。一种简单的思路是当每个结点入队时令该结点的所在层数设置为其父节点层数+1。这种方法很好理解也实现起来比较简单所以就不研究了。陈姥姥给出了另外一种解法,不需要在每个结点处都设置一个记录层数的变量,只需要一个tail,last,level这3个变量就可以解决这个问题了可以节省的空间为O(n)。
具体实现的思路,我按照上面这个手绘的图进行讲解:
首先,先访问顶点1,让顶点1入队,因为顶点1在第0层所有level=0,同时顶点1是第0层入队的最后一个元素所以last = 1;
然后让顶点1出队,按照编号从小到大访问顶点1的每个邻接点,每访问一个邻接点就把这个邻接点的编号赋值给tail,因此tail记录的实际上是当前最后一个被访问的顶点。当顶点1的所有邻接点访问完毕后,发现last == 顶点1,说明第0层的入队的所有元素都已经弹出,第0层所有入队的元素的所有邻接点都访问完毕,即第1层的所有元素都已经访问完毕,此时tail指向的是第一层的最后一个元素(顶点7),当第一层所有元素都被访问过后,很自然的level++表示第一层已经访问完毕。此时不要忘记让last = tail。因为last永远指向当前最后一个被访问元素的上一层的最后一个结点。
现在开始访问第3层,先让顶点2出队,把顶点2点所有邻接点都访问一遍,发现顶点2 != last(顶点7),因此让顶点3出队,把顶点3所有邻接点都访问一遍,发现顶点3 != last(顶点7),继续让顶点4出队... ... ... 最后让顶点7出队,把顶点7所有的邻接点访问一遍,这时候发现顶点7==last(顶点7),说明所有入队的第1层的顶点都已经出队且第2层的所有元素都已经被访问过了,这时候的tail应该指向的是顶点19,此时我们让level++,last = tail(19)。
然后开始访问第四层,先让顶点8出队... ... ...
... ... ... ...
当level==6的时候,我们就不需要继续BFS了,直接退出BFS即可。当然,这个题还要记录6层内的顶点数,我们只要设置一个count 变量初始化为0,在每次访问一个元素时count++即可。
下面是这个题的代码,配合图和讲解看会更容易理解。
#include<cstdio>
#include<queue>
using namespace std;
#define MAXV 1005
int n,G[MAXV][MAXV];
int BFS(int v,int vis[])
{
fill(vis,vis+MAXV,false);
int last,tail,level,w;
int count = 0;
queue<int> q;
vis[v] = true; count++;
q.push(v);
level = 0; last = v;//第0层最后一个入队的元素是v
/*进入核心循环*/
while(!q.empty())
{
v = q.front(); q.pop();//相当于dequeue()操作
for(w=1; w<=n; w++)
{
if(G[v][w]==1&&vis[w]==false)
{
vis[w] = true; count++;
q.push(w); tail = w;
}
}
if(v==last)
{
level++; last = tail;
}
if(level==6) break;
}
return count;
}
void SDS(int v,int vis[])
{
int count;
count = BFS(v,vis);
printf("%d: %.2f%%\n",v,count*100.0/n);
}
int main()
{
int m,v1,v2; int vis[MAXV];
scanf("%d%d",&n,&m);
for(int i=0; i<m; i++)
{
scanf("%d%d",&v1,&v2);
G[v1][v2] = 1;
G[v2][v1] = 1;
}
for(int v=1; v<=n; v++)
{
SDS(v,vis);
}
return 0;
}