【图】六度空间
题目要求:
“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”如图1所示。
“六度空间”理论虽然得到广泛的认同,并且正在得到越来越多的应用。但是数十年来,试图验证这个理论始终是许多社会学家努力追求的目标。然而由于历史的原因,这样的研究具有太大的局限性和困难。随着当代人的联络主要依赖于电话、短信、微信以及因特网上即时通信等工具,能够体现社交网络关系的一手数据已经逐渐使得“六度空间”理论的验证成为可能。
假如给你一个社交网络图,请你对每个节点计算符合“六度空间”理论的结点占结点总数的百分比。
输入格式:
输入第1行给出两个正整数,分别表示社交网络图的结点数N(1<N≤103,表示人数)、边数M(≤33×N,表示社交关系数)。随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个结点的编号(节点从1到N编号)。
输出格式:
对每个结点输出与该结点距离不超过6的结点数占结点总数的百分比,精确到小数点后2位。每个结节点输出一行,格式为“结点编号:(空格)百分比%”。
输入样例:
10 9
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
输出样例:
1: 70.00%
2: 80.00%
3: 90.00%
4: 100.00%
5: 100.00%
6: 100.00%
7: 100.00%
8: 90.00%
9: 80.00%
10: 70.00%
解题思路:
核心在于找到符合“六度空间”理论的结点。使用广度优先搜索,搜索六层即可。但是广度优先搜索的层数并不明晰。可以设置两个变量用来记录本层中的最后一个顶点和下一层的最后一个顶点,通过迭代和计数来计算六层的总顶点数。
注意:
- 每遍历一个顶点的六层后,需要重置所有顶点的访问标记。
- 顶点序号从1开始。
- 计算百分比,结果是一个float,参与运算的两个数是int,需要将分子int装换为float类型。
完整程序:
/*
【注意】
1.每遍历一个顶点的六层后,需要重置所有顶点的访问标记
2.顶点序号从1开始
3.计算百分比,结果是一个float,参与运算的两个数是int,需要将分子int装换为float类型
*/
/*
【解题思路】
核心在于找到符合“六度空间”理论的结点。使用广度优先搜索,搜索六层即可。但是广度优先搜索的层数并不明晰。
可以设置两个变量用来记录本层中的最后一个顶点和下一层的最后一个顶点,通过迭代和计数来计算六层的总顶点数。
*/
#include <cstdio>
#include <cstdlib>
const int MaxSize = 1001;
typedef int ElementType;
/*图结构(邻接矩阵)*/
typedef struct GNode *PtrToGNode;
struct GNode {
int Nv; // 顶点数
int Ne; // 边数
int G[MaxSize][MaxSize]; // 邻接矩阵
bool visited[MaxSize]; // 访问标志
};
typedef PtrToGNode MGraph;
/*边结构*/
typedef struct ENode *PtrToENode;
struct ENode {
int v1,v2; // 边的两端顶点序号
};
typedef PtrToENode Edge;
/*队列结构*/
typedef struct QNode *PtrToQueue;
struct QNode {
ElementType *Data;
int Front;
int Rear;
int Max; // 队列的最大长度(动态获取)
};
typedef PtrToQueue Queue; // 实现一个循环队列
MGraph BuildGraph(); // 建立图
MGraph CreateGraph(int vertexNum,int edgeNum); // 创建空图
void InsertEdge(MGraph graph,int v1,int v2); // 插入边
void ResetGraph(MGraph graph); // 重置图(重置所有顶点的访问标记)
int BFS(MGraph graph); // 宽度优先搜索(需要返回邻近六层的顶点数)
void SDS(MGraph graph); // 综合函数
Queue CreateQueue(int length); // 创建空队列
void InQueue(Queue Q,ElementType e); // 入队
ElementType OutQueue(Queue Q); // 出队
bool IsEmpty(Queue Q); // 队列判空
bool IsFull(Queue Q); // 队列判满
int main()
{
MGraph graph;
graph = BuildGraph();
SDS(graph);
return 0;
}
MGraph BuildGraph()
{
int N,M; // N:顶点数;M:边数
MGraph graph;
int v1,v2; // 插入边的两端顶点序号
int i;
scanf("%d %d",&N,&M);
graph = CreateGraph(N,M);
for(i = 0;i < M;i ++) {
scanf("%d %d",&v1,&v2);
InsertEdge(graph,v1,v2);
}
return graph;
}
// 只有顶点,没有边的空图
MGraph CreateGraph(int vertexNum,int edgeNum)
{
MGraph graph;
int i,j;
graph = (MGraph)malloc(sizeof(struct GNode));
graph -> Nv = vertexNum;
graph -> Ne = edgeNum;
for(i = 1;i <= vertexNum;i ++)
for(j = 1;j <= vertexNum;j ++)
{graph -> G[i][j] = 0; graph -> G[j][i] = 0;}
for( i = 1;i <= vertexNum;i ++)
graph -> visited[i] = false; // 全部没有被访问过
return graph;
}
void InsertEdge(MGraph graph,int v1,int v2)
{
// 无向图,所以两个顶点之间有一条边,则1-2间有一条,2-1间有一条
graph -> G[v1][v2] = 1;
graph -> G[v2][v1] = 1;
}
void ResetGraph(MGraph graph)
{
int i;
for(i = 1;i <= graph -> Nv;i ++)
graph -> visited[i] = false;
}
int BFS(MGraph graph,int v) // v:遍历起始顶点的序号;length:队列最大长度
{
int count = 1; // 记录遍历六层共访问的顶点数
Queue Q;
Q = CreateQueue(graph -> Nv);
int level = 1; // 记录当前遍历的层数
int last = v; // 记录当前层的最后一个顶点序号
int tail; // 记录当前层的下一层的最后一个顶点序号
int i;
InQueue(Q,v);
graph -> visited[v] = true; // 入队即访问过
while(!IsEmpty(Q)) {
v = OutQueue(Q);
for(i = 1;i <= graph -> Nv;i ++) {
if(graph -> G[v][i] && !graph -> visited[i]) {
InQueue(Q,i);
graph -> visited[i] = true;
count ++;
tail = i;
}
}
if(v == last) { // 弹出的顶点是当前层的最后一个顶点,说明该层遍历结束
level ++;
last = tail;
}
if(level > 6) break; // 注意是大于6时跳出循环
}
return count;
}
void SDS(MGraph graph)
{
int i;
float percent; // 记录每个顶点输出的百分比
for(i = 1;i <= graph -> Nv;i ++) {
percent = (float)BFS(graph,i) * 100 / graph -> Nv;
printf("%d: %.2f%\n",i,percent);
ResetGraph(graph);
}
}
Queue CreateQueue(int length)
{
Queue Q;
Q = (Queue)malloc(sizeof(struct QNode));
Q -> Data = (ElementType *)malloc(length * sizeof(ElementType));
Q -> Front = Q -> Rear = 0;
Q -> Max = length;
return Q;
}
void InQueue(Queue Q,ElementType e)
{
if(IsFull(Q))
return;
Q -> Data[Q -> Rear] = e;
Q -> Rear = (Q -> Rear + 1) % Q -> Max;
}
ElementType OutQueue(Queue Q)
{
ElementType tmp; // 接收出队元素
if(IsEmpty(Q))
return -1;
tmp = Q -> Data[Q -> Front];
Q -> Front = (Q -> Front + 1) % Q -> Max;
return tmp;
}
bool IsEmpty(Queue Q)
{
return (Q -> Front == Q -> Rear);
}
bool IsFull(Queue Q)
{
return (Q -> Front == (Q -> Rear + 1) % Q -> Max);
}