可图画,连通图和欧拉图的判断

可图画,连通图和欧拉图的判断

语言使用c++

实验目的:

1、掌握可简单图化的定义及判断方法;
2、掌握连通图、欧拉图的判断方法;
3、掌握欧拉回路的搜索方法;
4、了解欧拉图的实际应用。

实验要求

1、给定一非负整数序列(例如:(4,2,2,2,2))。
2、判断此非负整数序列是否是可图化的,是否是可简单图化的。
3、如果是可简单图化的,根据Havel定理过程求出对应的简单图,并输出此简单图的相邻矩阵(默认第i行对应顶点vi)。
4、判断此简单图是否是连通的。
如果是连通图,判断此图是否是欧拉图。如果是欧拉图,请输出一条欧拉回路(输出形式如:v2->v1->v5->v3->v4->v5->v2)。

输入限定(较为简单的部分,不做解释)

先讲一下这里实现了一个点类

class point
{
private:
	int* other; //数组
	int pointId;//点的名字 v1/v2/....
	int border;//这个点有几条边
	int s;
public:
	
	point() {
		border = 0;
		pointId = 0;
		other = NULL;

	}

	void setpoint(int n, int id,int bor)
	{
		other = new int[n]();
		
		pointId = id;
		border = bor;
		s = n;
	}


	int getborder()
	{
		return border;
	}

	void setother(int i)
	{
		//cout << pointId<<endl;
	
		other[i] = 1;
	}

	void printSelf()
	{
		for (int i = 0; i < s; i++)
		{
			cout << other[i]<<" ";
		}
		cout << endl;
	}
	int* returnOther()
	{
		return other;
	}

	int returnCon(int i)
	{
		return other[i];
	}
};

这个数组就是为了判断这个点和剩下的点是否联通,联通为1,不连通为0;

1.实现判断简单图

两条判定定理 首先使用握手定理 在判断最大度小于n
(因为是(4,2,2,2,2)这样输入的,所以去一个字符把逗号读走,最后读到)结束

	while (Yes != 41)//先读进来
	{
		cin >> n>> Yes;
		arr[i] = (int)n;
		i++;
		sum += n;

	}
bubble_sort(arr, i);//冒泡
if(sum%2==0)
	{
		if (arr[0] > i - 1)
		{
			cout << "不可图画" << endl;
			return 0;
		}
		

#2.用fleury判断是否可简单图画并且生成临接矩阵


	int big;//主要的指针
	int tmp;//每次右移的数量
	
	int simple = 0;//0代表可 1代表不可
	for (big = 0; big < i-1; big++)
	{
		tmp=arr[big];
		arr[big] = 0;
			for (int z=0; z < tmp; z++)
			{
				arr[big + z + 1]--;
				//建立边的联系,应该是交换完的数组应该在反过来
				/*cout << position[big + z + 1]<<"++"<<endl;*/
				points[position[big + z + 1]].setother(position[big]);
				points[position[big]].setother(position[big + z + 1]);

				

		}
			//检查剩下的数有没有负数
			for (int a = big+1; a < i; a++)
			{
				if (arr[a] < 0)
				{
					simple = 1;
					cout << "可图画,不可简单图画" << endl;
					return 0;
					break;

				}
			}
			//把数组在排序一下
			newbubble_sort(arr, position, i - big+1, big + 1);
			if (simple == 1) break;
			




	}
	}
	else
	{
		cout << "不可图画"<<endl;
		return 0;
		simple = 1;
	}


	/*for (int a = 0; a < i; a++)
	{
		points[a].printSelf();
	}*/
	if (simple == 0)
	{
		for (int a = 0; a < i; a++)
		{
			points[a].printSelf();
		}
	}

这里主要讲下思路,这里定义了两个数组用来代替二维数组,然后设置一个点类(所以写算法时最好别写类,很难写),我们在上一步已经已经使用冒泡排序,所以现在是一个从大到小的数组,排序完要实例化对象生成n个点
对于输入样例:
(4,2,2,2,2)来说,
创建对象 v1有四条边,v2有两条边,v3,v4…

我们要在建立一组数组,来存放他的位置(判断交换位置后到底是v几),因为后面减的过程中可能会出现后面的比前面的大,这时候就要重新排序,所以为了防止位置错误,这里新建一个position数组
例:arr[4,2,2,2,2]
position[0,1,2,3,4]

都创建完成后我们开始进入循环
我们首先应该读到 4 也就是对应数组arr[0],然后对于后面4个点来说 ,每个点的度-1,并且说明后面四个点和度为4的点有连接.这时候要在点类中设置,就是说 v2,v3,v4,v5都要用setborde方法和v1建立联系,同样的v1和他们的联系也要set

points[position[big + z + 1]].setother(position[big]);
points[position[big]].setother(position[big + z + 1]);

这里为什么是position[big]?
此时没有交换 后面会讲这个问题.

然后一个过程就完成了,此时把不要的放弃对除第一点以外的点排序,注意数组的长度也变了.
此时应改为
arr[1,1,1,1]
position[1,2,3,4]
在进行一部循环
此时剩下数为:
arr[0,1,1]
position[2,3,4]
此时来讲为什么要用position这个数组,我们对他排序完
arr[1,1,0]
position[3,4,0]
这是我们用position[n]就能知道到底是哪两个点之间有联系,不然就会变成v3和v4之间的联系就错了.

2.判断是否为联通图

可能打竞赛的同学知道很多方法,这里我说两个普通吧,毕竟我也不打不懂啊

简单的方法就说算连通矩阵的n-1次方,除对角线以为不可能出现一个0,也可以在简单一点对于一行,除它自己以外存在一个0,说明是不连通的,但是c++实现矩阵的乘法也太麻烦了(不太懂的同学可以看看离散数学书)

可以参考第二种方法
我提供思路和部分代码讲解,因为我实在写的太不规范了,如果后面有时间我会重写一个简单的

//上代码
void check(matrix* A, int i,int arr[])
{
	for (int a=0; a < i; a++)
	{
		int f = 0, l = 0,success;
		if (arr[a] == 1)
		{
			f = 0;
			while (true)
			{
				if (A->get(f, l) != 0)
				{
					success = 0;
					int tmp1 = f;
					A->setdate(f, l, 0);
					A->setdate(l, f, 0);
					arr[l] = 1;
					check(A, i, arr);
					f = l;
					//如果过去什么也没有 在回来
					for (int a = 0; a < i; a++)
					{
						if (A->get(f, a) != 0)
						{
							success = 1;
							break;

						}
					}
					if (success == 0) f = tmp1;
					l = 0;
				
					
					
				}
				else
				{

					l++;
					if (l == i) break;

				}


			}
		}
	}
}
int recursive(matrix* A,int i)
{
	int f = 0, l = 0,n=0;
	int success;
	int *judge;
	judge = new int[i]();
	
	judge[0] = 1;
	while (true)
	{
		if (A->get(f, l) != 0)
		{
			success = 0;
			int tmp1 = f;
			A->setdate(f, l, 0);
			A->setdate(l, f, 0);
			judge[l] = 1;
			f = l;
			//如果过去什么也没有 在回来
			for (int a = 0; a < i; a++)
			{
				if (A->get(f, a) != 0)
				{
					success = 1;
					break;

				}
			}
			if (success == 0) f = tmp1;
			l = 0;
			
			
		}
		else
		{

			l++;
			if (l == i) break;

		}


	}


	check(A, i, judge);

	
	for (int a = 0; a < i; a++)
	{
	
		if (judge[a] == 0)
				return 1;
		
	}
	return 0;
}

就是一个简单的递归
讲一下主要思路,我们都从v1开始找,如果找完还有v1不能连通到的地方,就说明是不连通的,我们定义一个数组judge[]只要是能找到我们都设置为1

首先v1是1,judge[0]=1;然后进去找v1相连的边;我们还是拿(4,2,2,2,2)来讲
首先看着联通矩阵
0 1 1 1 1
1 0 1 0 0
1 1 0 0 0
1 0 0 0 1
1 0 0 1 0
v1 找到和v2 是相连的就把这个边删了,所以消除(0,1)和(1,0)的1
并且这里judge[1]=1;然后找v2的
0 0 1 1 1
0 0 1 0 0
1 1 0 0 0
1 0 0 0 1
1 0 0 1 0
找完再找v3
0 0 1 1 1
0 0 0 0 0
1 0 0 0 0
1 0 0 0 1
遍历完成后,我们需要考虑如果v1->v2->v3 但v2->v4;
我们没有考虑到这种情况,所以我们对我们之要在进行循环判断我们之前这个连通路径还能不能有其他路径
这里使用了递归调用,如果我们发现的一个新的点,这个点可能会连通很多点,所以还有对这个点进行递归.(这里后面找路径也是这种思路)

最后但我们不能在发现新的点退出,如果这个数组还有0,则不能连通;

判断是否为欧拉图

很简单不多说

找到一条欧拉回路

这里前面找连通的时候给了我思路,这里我们使用欧拉图的一个冲要条件,n个不相交的圈的并,我们如果像上面的方法一样,因为是欧拉图,所以我们一定找到一条回路,此时我们就把当作大圈
在这里插入图片描述
然后遍历这个圈上所有的点,要是有新的路径可以开始,也一定是个圈,当存在一个新圈的时候要在找一遍所以要进行递归

void move(int* first, int star,int n)
{
	for (int i = 0; i < n; i++)
	{
		first[star + i + n] = first[star + n];
	}
}
void add(int* first, int *last, int n,int sum)
{
	int i;
	n = last[0];
	for (i = 0; i < sum; i++)
	{
		if (first[i] == last[0])
		{
			move(first, i, n);
			for (int a = 0; a < n; a++)
			{
				first[i + a] = last[a];
			}
		}
	}
}

void checkroad(matrix* A, int i, int arr[],int road[],int sum)
{
	int f = 0, l = 0, success=0, n = 0;
	int* tmp;
	tmp = new int[sum]();
	for (int a = 0; a < i; a++)
	{
		
		if (arr[a] == 1)
		{
			f = a;
			n = 0;
			tmp[n] = a;
			while (true)
			{
				if (A->get(f, l) != 0)
				{
					success = 1;
					int tmp1 = f;
					A->setdate(f, l, 0);
					A->setdate(l, f, 0);
					arr[l] = 1;
					
					f = l;
					n++;
					tmp[n] = l+1;
					
					l = 0;



				}
				else
				{

					l++;
					if (l == i)
					{
						l = 0;
						break;
					}

				}


			}
		}

		if (success == 1)
		{
			checkroad(A, i, arr, road, sum);
			//把这个新数组加进来
			add(road, tmp, n, sum);

		}

	}
}
void findroad(matrix* A, int i, int road[],int sum)
{
	int f = 0, l = 0, n = 0;
	int success;
	int* judge;
	judge = new int[i]();
	road[0] = 1;
	judge[0] = 1;
	while (true)
	{
		
		if (A->get(f, l) != 0)
		{
			n++;
			road[n] = l+1;
			success = 0;
			int tmp1 = f;
			A->setdate(f, l, 0);
			A->setdate(l, f, 0);
			judge[l] = 1;
			f = l;
			l = 0;
			//如果过去什么也没有 就停下来
			for (int a = 0; a < i; a++)
			{
				if (A->get(f, a) != 0)
				{
					success = 1;
					break;

				}
			}
			if (success == 0)
			{
				
				break;
			}
			

		}
		else
		{

			l++;
			if (l == i) break;

		}


	}


	checkroad(A, i, judge,road,sum);


	
}

当然还有找到路径还得存下来,所以还有一下修改数组的方法

最后看看结果

在这里插入图片描述
第一次写博客可能还有很多不足,后面有时间会整理了代码放github上

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值