通信网络(图的应用)

写在前面:仅为个人代码/总结,未必标准,仅供参考!如有错误,还望指出交流,共同进步!

通信网络

【问题描述】(CSP原题)
某国的军队由N个部门组成,为了提高安全性,部门之间建立了M条通路,每条通路只能单向传递信息,即一条从部门a到部门b的通路只能由a向b传递信息。信息可以通过中转的方式进行传递,即如果a能将信息传递到b,b又能将信息传递到c,则a能将信息传递到c。一条信息可能通过多次中转最终到达目的地。
由于保密工作做得很好,并不是所有部门之间都互相知道彼此的存在。只有当两个部门之间可以直接或间接传递信息时,他们才彼此知道对方的存在。部门之间不会把自己知道哪些部门告诉其他部门。
在这里插入图片描述
上图中给了一个4个部门的例子,图中的单向边表示通路。部门1可以将消息发送给所有部门,部门4可以接收所有部门的消息,所以部门1和部门4知道所有其他部门的存在。部门2和部门3之间没有任何方式可以发送消息,所以部门2和部门3互相不知道彼此的存在。
  现在请问,有多少个部门知道所有N个部门的存在。或者说,有多少个部门所知道的部门数量(包括自己)正好是N。
【输入形式】
输入的第一行包含两个整数N, M,分别表示部门的数量和单向通路的数量。所有部门从1到N标号。
接下来M行,每行两个整数a, b,表示部门a到部门b有一条单向通路。
【输出形式】
输出一行,包含一个整数,表示答案。

【问题分析】
① 处理对象:N个整数和M对表示边的整数。
② 实现的功能:基于图的ADT计算知道所有N个部门的存在的部门数量。
③ 结果的显示:输出知道所有N个部门的存在的部门数量。
【抽象数据类型设计】&&【物理数据对象设计】
结构特征:图
图的ADT:
在这里插入图片描述
【算法思想设计】
(说明:此解法只适用于测试数据规模较小的情况,仅作为联系应用图的ADT的载体,提交到CSP系统可能只会判30分,当然也可用Warshall算法解题)
通过验证图中每个顶点到其他点的连通性来判断知道所有N个部门的存在的部门数量:
首先定义图的ADT和继承类Graphm的设计,根据输入所给顶点和各边,基于图的邻接矩阵建立图各顶点的对应关系从而构建图。然后在Graphm类中增加声明一个数组num,用于后继作为表示某顶点到图中其他各点是否连通的标记,然后对于每个顶点,根据邻接矩阵通过DFS1深度优先检索依照边的方向正向(可以向其他点发出讯息)搜索与该点连通的其他各点并在num数组中做好标记,通过DFS2深度优先检索依照边的方向反向(可以接收到其他点发出讯息)搜索与该点连通的其他各点并在num数组中做好标记,最后遍历num数组中的元素,若发现有该点与其他某点不连通的标记,在表示该部门不知道所有N个部门的存在,否则表示该部门知道所有N个部门的存在。

【题目样例基于设计算法的求解过程】
根据输入构建以下图:
在这里插入图片描述其邻接矩阵为:
在这里插入图片描述
正向搜索:从1可达 2、3、4,从2可达4,从3可达4。
反向搜索:从4可达1、2、3,从2可达1,从3可达1。
则可以的到3与2不连通,故知道有4个部门存在的只有1、4两个部门。

【关键实现部分的算法步骤】
① 构建图的各边:setEdge(int v1, int v2, int wt)函数:
由于实验使用的是基于邻接矩阵实现图的ADT,因此在建立边时直接将邻接矩阵中对应位置置为边的权值,这里都赋值为1。
②用dfs1(int i)函数实现从某一点出发正向搜索可达到的其他的点,伪代码如下:

void dfs1(int i)
    {
    
    
            setMark(i,VISITED);//做顶点访问标记
            for(int j=0;j<numVertex;j++)
            {
    
    
                if(matrix[i][j]==1)
                {
    
    
                    num[j]=1;//做可到达顶点的标记
                    if(getMark(j)==UNVISITED)
//根据下一顶点是否已访问过作为递归条件,避免由于图中有环出现死循环
                    {
    
    
                        dfs1(j);//递归搜索
                    }

                }
            }
    }

③用dfs2(int i)函数实现从某一点出发反向搜索可达到的其他的点,伪代码如下:

void dfs2(int i)
    {
    
    
            setMark(i,VISITED); //做顶点访问标记
            for(int j=0;j<numVertex;j++)
            {
    
    
                if(matrix[j][i]==1)//相当于转置邻接矩阵
                {
    
    
                    num[j]=1; //做可到达顶点的标记
                    if(getMark(j)==UNVISITED)
//根据下一顶点是否已访问过作为递归条件,避免由于图中有环出现死循环
                    {
    
    
                        dfs2(j); //递归搜索
                    }
                }
            }
    }

④判断某一点是否与其他各点连通,伪代码如下:

bool countnum(int j)
    {
    
    
        bool flag=true;
        for(int i=0;i<numVertex;i++)
        {
    
    
            if(num[i]==0&&i!=j)//排除自身与自身连通的情况同时判断是否有不连通的点
            {
    
    
                flag=false;break;
            }
        }
        return flag;
}

⑤main函数中关键部分的实现:

int res=0;//知道所有N个部门的存在的部门数量
    for(int i=0;i<N;i++)
    {
    
    
        G.resetmark();
        G.resetnum();//对每一个点开始搜索前清除连通标记
        G.dfs1(i);//正向寻找从该点沿边能到达的其他点
        G.resetmark();//清除各点访问标记
        G.dfs2(i);//反向寻找从该点沿边能到达的其他点
        bool flag=G.countnum(i);//表示该点是否与图中其他所有点连通
        if(flag==true)
        {
    
    
            res++;
        }
    }
    cout<<res<<endl;

猜你喜欢

转载自blog.csdn.net/qq_45909595/article/details/115286382