离散数学Ⅱ-生成树、环路空间、断集空间的求解

 实验目的

  1. 掌握无向连通图生成树的求解方法;
  2. 掌握基本回路系统和环路空间的求解方法;
  3. 掌握基本割集系统和断集空间的求解方法;
  4. 了解生成树、环路空间和断集空间的实际应用。

实验要求

1.给定一无向简单连通图的相邻矩阵

2.输出此图的关联矩阵M

3.求此图所有生成树的个数。

4.输出其中任意一颗生成树的相邻矩阵(默认第i行对应顶点vi)和关联矩阵(默认第i行对应顶点vi,第j列对应边ej)。

5.求此生成树对应的基本回路系统(输出形式如:{e1e4e3,e2e5e3})。

6.求此生成树对应的基本割集系统(输出形式如:{{e1,e4},{e2,e5},{e3,e4,e5}})。

加分题

求此生成树对应的环路空间

求此生成树对应的断集空间

思路分析

这里用一个样例给大家简单输出一下

0 1 1 1
1 0 0 1
1 0 0 1
1 1 1 0

第一问

输出关联矩阵

较为简单,不多讲解,这里也应该卡一个输入输出限制,不能检查有没有负数,判断是不是简单图

for (int l = 0; l < line; l++)
	{
		for (int h = 0; h < column; h++)
		{
			cin >> p[l][h];
			if (p[l][h] < 0)
			{
				cout << "存在负数 请重新输入"<<endl;
				return 0;
			}
			sumD += p[l][h];
		}
	}

	int edgesNumber;
	if (sumD % 2 == 1)
	{
		cout << "矩阵有误,请重新输入";
		return 0;
	}

这里还设置一个边类,这个边,内存放连接的两个点,和这个边的id(它是e几)

class borde
{
public:
	int v1;
	int v2;
	int id;
	borde()
	{

	}

};

准备工作做好以后就开始转换

先把相邻矩阵有1的地方转换为边(这个非0点的二维坐标就是连接的两个边)//注意在代码里是e0开始 所有输出的时候加个1.

borde* mborde = new borde[edgesNumber];
	//检查是相邻矩阵是否为1
	int n = 0;//计数
	for (int l = 0; l < line; l++)
	{

		for (int h = l + 1; h < column; h++)
		{
			if (p[l][h] == 1)
			{
				mborde[n].v1 = l;
				mborde[n].v2 = h;
				mborde[n].id = n + 1;
				n++;
			}
		}

	}

然后对应着把这个边数组,存进二维数组里。

int** associated;
	column = edgesNumber;
	associated = new int* [line];
	for (int i = 0; i < line; i++)
	{
		associated[i] = new int[column]();
	}

	for (int x = 0; x < column; x++)
	{
		associated[mborde[x].v1][x] = 1;
		associated[mborde[x].v2][x] = 1;
	}
	cout << endl;

结果

这里从0-4是 e1-e5;

求生成树的数量

        首先,这里用了离散数学书上的方法,也就是说,我们需要一个n-1阶的基本关联矩阵,那我们就只要这个矩阵的n-1行 这里需要n-1列,所以这里需要排列组合 ,大家可以上网搜搜排列组合,我是直接用到别人的,就不放出来了。

这里我们应该得到 C3/5 的所有组合数

这个数代表上面那个关联矩阵的列数,所有拿第一个举例 0 3 4 对应 e1 e4 e5

然后去3*3的一个矩阵 判断这个二维数组的行列式 非0则为生成数 遍历一下判断有几个数;

 随便取组作为生成的树输出

 相邻矩阵这里不多讲

 求此生成树对应的基本回路系统

这里使用的一个不是很好理解的方法

先讲一个简单的方法,

首先要知道基本回路系统只有一个圈,我们这里用度数为2来判断,一定有弦的数量个回路,而且一定不一样.开始遍历,找一条弦和任意一条边,判断度数是否为2,不是就在换,这里组合求法和上一问一样,这里是从2条边开始判断,两条不行就三条,依次上加,然后路径要按一个圈的顺序来,我们知道这是一个回路 可以用上一提找欧拉回路的办法,这里因为已经知道圈的大小,所以直接从第一开始遍历就行。理解这种想法,下一问用了类似的

这里是我的方法(很抽象而且很没必要)

这里用了关联矩阵的递归,而且我目前写的这个方法还有点bug,不过懒的改了,在递归调用退出的时候有点小问题,

这是我们取到的任加一个弦的二维数组,此时想法从e2的一个顶点出发回到e2的另外一个顶点结束,因为这个圈里一定包含e2,所有一定能找到,我们读到e2的第一点 也就是v1,从v1出发 发现了e1 此时应该通过e1找到 连接的另外一个顶点,然后开始递归调用,

这里重点讲一下递归的结束条件,首先就是找到 也就是着一行的最后一个数是1,当然这不可能是起始的顶点,(这里不做多的判断 因为不可回到第一个顶点,不然就有圈了 它就不是生成树了)

第二种可能是这是个悬挂点 这一行只要1个1,显然回退出返回上一次调用

两种情况判断完以后,就开始找这一行非自己的1,在准备进入下一行;

int start(int ** tmpRoad,int line,int firstX,int lastY,int *road,int number)//要一个二维数组
{

	for (int l = 0; l < line ; l++)
	{
		int quite = 0;
		if (tmpRoad[l][lastY] == 1 && l != firstX)
		{
			//找到先判断是不是终点
			if (tmpRoad[l][line-1]==1)
			{
				//就是找到了
				road[0] = number;
				road[number] = lastY;
				return 1;
			}
			//在判断这个点是不是悬挂点
			int Ok=0;
			for (int i=0; i < line - 1; i++)
			{
				if (tmpRoad[l][i]==1&&i!=lastY)
				{
					Ok = 1;
					break;
				}
			}
			if (Ok!=1)
			{
				return 0;//直接返回
			}
			else
			{
				//找到下一个点并且开始递归
				for (int i=0; i < line-1; i++)
				{
					if (tmpRoad[l][i] == 1 && i != lastY)
					{
						quite=start(tmpRoad, line, l, i, road, number + 1);
						if (quite==1)
						{
							road[number] = lastY;
							return 1;
						}
					}
				}
			}


		}
	}
}

这个就是递归的子函数,有bug,大家可以修改,bug就在第二个判断时,当这个点时悬挂点时,我们应该返回上一层继续判断下一个点,我这里写的时候应该直接出去了,直接全部return了,有问题,不过问题不大,

附加题的第一问做环合运算就行,不多讲

最后一问


    //最后一问;
    //选中生成树的一条边 
    //从弦中找一下 1/2/3/n删除 若删完不连通 则为一个断集
    //一个二维数组存最后的结果 第一个数是这个数组的长度 

这个和上一问的简单方法想法一样,就是选一个树枝,然后找任意一个弦,把这个两个都删了,判断矩阵连通不,不连通在换一条边,一条不行就放两条,这里连通的方法和上篇博客的方法一样,大家可以取看看,

附加的第二问也是做个环合运算就行

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值