数据结构课设计:多叉路口交通灯管理问题

2021/03/28 17:32
阅读数 785

题目描述
通常,在十字交叉路口只需设红、绿两色的交通灯便可保持正常的交通秩序,而在多叉路口需设几种颜色的交通灯才能既使车辆相互之间不碰撞,又能达到车辆的最大流通。假设有一个如图(a)所示的五叉路口,其中C和E为单行道。在路口有13条可行的通路,其中有的可以同时通行,如A→B和E→C,而有的不能同时通行,如E→B和A→D。那么,在路口应如何设置交通灯进行车辆的管理呢?

在这里插入图片描述
思路分析
顶点图解释:
1.圈XY代表从道路X驶向道路Y的通道。
2.共有2×C(3,2)+4+3共13条通道。
3.若两通道不能同时通行,则将两通道相连。

在这里插入图片描述

问题转化为图着色问题
用最少的颜色对图着色<=>对尽可能多的点着以相同的颜色<=>让尽可能多的通道的车辆可以同时通行=>车流量最大

邻接矩阵
按图里从左到右从上到下的顺序对13个顶点从0~12依次标号,然后按照两顶点之间有边相连值为1,无边相连值为0的规则,得到邻接矩阵,如下:
0 0 0 0 1 1 1 0 0 1 0 0 0
0 0 0 0 0 1 1 1 0 1 1 0 0
0 0 0 0 0 0 0 0 0 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 1 0 0 1 0 0
1 1 0 0 0 0 1 0 0 0 1 1 0
1 1 0 0 0 1 0 0 0 0 1 1 0
0 1 0 0 1 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 1 1 1 0 0 0 0 0 0
0 0 1 0 0 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0

算法
算法1:回溯法(深度优先搜索)
思路:

1.假设符合题意的最小颜色数是m
2.则每一个顶点可以涂m种颜色,共m^13种可能(包括合理和不合理的涂色方案)
3.采用深度优先搜索算法,找到合理的涂色方案,如果找不到则增加最小颜色数继续查找
该算法一定得到最优解,但时间复杂度高。


算法2:贪婪算法(Welch Powell算法)
1.将顶点按度数由大到小排列
2.对未涂色的度数最大的顶点涂色,并将与该顶点不相连且满足条件的其它顶点涂以相同的颜色
3.检查是否存在未涂色的顶点,若有,换一种颜色,重复2),否则,结束算法
该算法是启发式算法,时间复杂度低,但不一定得到最优解。
算法源码
算法1

#include <iostream>
using namespace std;

//颜色类 
class Color {
	public:
		Color(int n,int **a,int *x,int sum);
		bool ifOk(int node);
		void Dfs(int node);
		int n;		//顶点数
		int m;		//可用颜色数
		int **a;	//邻接矩阵
		int *x;		//解向量
		int sum;	//可着色方案数
};

//构造函数 
Color::Color(int n,int **a,int *x,int sum){
	this->n=n;
	this->a=a;
	this->x=x;
	this->sum=sum;		
}

//判断顶点node是否可以涂色 
bool Color::ifOk(int node) {
	for (int i = 1; i <= n; i++) 
		if ((a[node-1][i-1] == 1) && x[node] == x[i])
			return false;
	return true;
}

//深度优先搜索 
void Color::Dfs(int node) {
	//当访问到叶子结点,则找到一种可着色方案 sum++
	if (node > n) {
		sum++;
		//输出该涂色方案 
		for (int i = 1; i <= n; i++)
			cout << x[i] << " ";
		cout << endl;
	}
	//递归 
	else {
		for (int i = 1; i <= m; i++) {
			x[node] = i;
			if (ifOk(node))
				Dfs(node + 1);
			x[node] = 0;
		}
	}
}

int main() {
	//初始化邻接矩阵 
	int** graph=new int*[13];
	for(int i=0;i<13;i++)
		graph[i]=new int[13];
	for (int i = 0;i < 13;i++)
		for (int j = 0;j < 13;j++)
			cin >> graph[i][j];
	
	//初始化解向量 
	int* p=new int[14];
	for(int i=0;i<14;i++)
		p[i]=0;
	
	//初始化颜色类 
	Color color(13,graph,p,0);
	
	//涂色 
	int k=0;
	while(true){
		k++;
		color.m=k;
		color.Dfs(1);
		if(color.sum!=0)
			break;
	}
	
	//输出最小颜色数和涂色方案数 
	cout<<"最小颜色数m为:"<<k<<endl; 
	cout<<"可行的涂色方案数为:"<<color.sum;
}

编译运行:

$ g++ traffic11.cpp
$./a.out
0 0 0 0 1 1 1 0 0 1 0 0 0
0 0 0 0 0 1 1 1 0 1 1 0 0
0 0 0 0 0 0 0 0 0 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 1 0 0 1 0 0
1 1 0 0 0 0 1 0 0 0 1 1 0
1 1 0 0 0 1 0 0 0 0 1 1 0
0 1 0 0 1 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 1 1 1 0 0 0 0 0 0
0 0 1 0 0 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0

......................................
4 4 4 4 3 3 1 1 3 1 2 2 1 
4 4 4 4 3 3 1 1 3 1 2 2 2 
4 4 4 4 3 3 1 1 3 1 2 2 3 
4 4 4 4 3 3 1 1 3 1 2 2 4 
4 4 4 4 3 3 1 1 3 2 2 2 1 
4 4 4 4 3 3 1 1 3 2 2 2 2 
4 4 4 4 3 3 1 1 3 2 2 2 3 
4 4 4 4 3 3 1 1 3 2 2 2 4 
4 4 4 4 3 3 1 1 3 3 2 2 1 
4 4 4 4 3 3 1 1 3 3 2 2 2 
4 4 4 4 3 3 1 1 3 3 2 2 3 
4 4 4 4 3 3 1 1 3 3 2 2 4 
4 4 4 4 3 3 1 1 4 1 2 2 1 
4 4 4 4 3 3 1 1 4 1 2 2 2 
4 4 4 4 3 3 1 1 4 1 2 2 3 
4 4 4 4 3 3 1 1 4 1 2 2 4 
4 4 4 4 3 3 1 1 4 2 2 2 1 
4 4 4 4 3 3 1 1 4 2 2 2 2 
4 4 4 4 3 3 1 1 4 2 2 2 3 
4 4 4 4 3 3 1 1 4 2 2 2 4 
4 4 4 4 3 3 1 1 4 3 2 2 1 
4 4 4 4 3 3 1 1 4 3 2 2 2 
4 4 4 4 3 3 1 1 4 3 2 2 3 
4 4 4 4 3 3 1 1 4 3 2 2 4 
4 4 4 4 3 3 2 2 1 1 1 1 1 
4 4 4 4 3 3 2 2 1 1 1 1 2 
4 4 4 4 3 3 2 2 1 1 1 1 3 
4 4 4 4 3 3 2 2 1 1 1 1 4 
4 4 4 4 3 3 2 2 1 2 1 1 1 
4 4 4 4 3 3 2 2 1 2 1 1 2 
4 4 4 4 3 3 2 2 1 2 1 1 3 
4 4 4 4 3 3 2 2 1 2 1 1 4 
4 4 4 4 3 3 2 2 1 3 1 1 1 
4 4 4 4 3 3 2 2 1 3 1 1 2 
4 4 4 4 3 3 2 2 1 3 1 1 3 
4 4 4 4 3 3 2 2 1 3 1 1 4 
4 4 4 4 3 3 2 2 2 1 1 1 1 
4 4 4 4 3 3 2 2 2 1 1 1 2 
4 4 4 4 3 3 2 2 2 1 1 1 3 
4 4 4 4 3 3 2 2 2 1 1 1 4 
4 4 4 4 3 3 2 2 2 2 1 1 1 
4 4 4 4 3 3 2 2 2 2 1 1 2 
4 4 4 4 3 3 2 2 2 2 1 1 3 
4 4 4 4 3 3 2 2 2 2 1 1 4 
4 4 4 4 3 3 2 2 2 3 1 1 1 
4 4 4 4 3 3 2 2 2 3 1 1 2 
4 4 4 4 3 3 2 2 2 3 1 1 3 
4 4 4 4 3 3 2 2 2 3 1 1 4 
4 4 4 4 3 3 2 2 3 1 1 1 1 
4 4 4 4 3 3 2 2 3 1 1 1 2 
4 4 4 4 3 3 2 2 3 1 1 1 3 
4 4 4 4 3 3 2 2 3 1 1 1 4 
4 4 4 4 3 3 2 2 3 2 1 1 1 
4 4 4 4 3 3 2 2 3 2 1 1 2 
4 4 4 4 3 3 2 2 3 2 1 1 3 
4 4 4 4 3 3 2 2 3 2 1 1 4 
4 4 4 4 3 3 2 2 3 3 1 1 1 
4 4 4 4 3 3 2 2 3 3 1 1 2 
4 4 4 4 3 3 2 2 3 3 1 1 3 
4 4 4 4 3 3 2 2 3 3 1 1 4 
4 4 4 4 3 3 2 2 4 1 1 1 1 
4 4 4 4 3 3 2 2 4 1 1 1 2 
4 4 4 4 3 3 2 2 4 1 1 1 3 
4 4 4 4 3 3 2 2 4 1 1 1 4 
4 4 4 4 3 3 2 2 4 2 1 1 1 
4 4 4 4 3 3 2 2 4 2 1 1 2 
4 4 4 4 3 3 2 2 4 2 1 1 3 
4 4 4 4 3 3 2 2 4 2 1 1 4 
4 4 4 4 3 3 2 2 4 3 1 1 1 
4 4 4 4 3 3 2 2 4 3 1 1 2 
4 4 4 4 3 3 2 2 4 3 1 1 3 
4 4 4 4 3 3 2 2 4 3 1 1 4 
最小颜色数m为:4
可行的涂色方案数为:92160

算法2:

#include <iostream>
#include <queue> 
using namespace std;
struct Node {
	int index;
	int degree;
	int color;
};

//冒泡排序 
void sort(Node* nodes) {
	for (int i = 0;i < 13;i++) {
		for (int j = 12;j > i;j--) {
			if (nodes[j].degree > nodes[j - 1].degree) {
				int degree = nodes[j - 1].degree;
				int index = nodes[j - 1].index;
				nodes[j - 1].degree = nodes[j].degree;
				nodes[j - 1].index = nodes[j].index;
				nodes[j].degree = degree;
				nodes[j].index = index;
			}

		}
	}
}

//按度数递减顺序输出排序后的顶点 
void output(Node* nodes){
	queue<int> q0,q1,q2,q3,q4,q5;
	queue<int> q[6]; 
	for(int i=0;i<13;i++)
		q[nodes[i].degree].push(nodes[i].index);
	for(int i=5;i>=0;i--){
		cout<<"度数为"<<i<<"的顶点:";
		if(q[i].empty())
			cout<<"无";
		while(!q[i].empty()){
			int index=q[i].front( );
			cout<<index<<" ";
			q[i].pop();
			} 
			cout<<endl;
		}
}

//判断顶点nodes[j]是否可以涂上颜色color 
bool ifOk(Node* nodes,int** graph,int color,int j){
	if(nodes[j].color!=0)
		return false;
	for(int i=0;i<13;i++)
			if(graph[nodes[j].index][i]==1)
				for(int m=0;m<13;m++)
					if(nodes[m].index==i)
						if(nodes[m].color==color)
							return false;
	return true;
} 

int main() {
	//初始化邻接矩阵 
	int** graph=new int*[13];
	for(int i=0;i<13;i++)
		graph[i]=new int[13];
	//初始化顶点数组,记录13个顶点的索引、度数、颜色	
	Node* nodes = new Node[13];
	for (int i = 0;i < 13;i++) {
		int degree = 0;
		for (int j = 0;j < 13;j++) {
			cin >> graph[i][j];
			if (graph[i][j])
				degree++;
		}
		nodes[i].index = i;
		nodes[i].degree = degree;
		nodes[i].color = 0;
	}
	
	//对顶点按度数递减排序 
	sort(nodes);
	//输出排序后的结果 
	cout<<"按度数由大到小排序后的结果:"; 
	for(int i=0;i<13;i++){
		cout<<nodes[i].index<<" ";
		if(i==12)
			cout<<endl;
	}
	output(nodes);
	
	//对13个顶点采用贪婪算法进行迭代涂色 
	int k = 0;
	while (true) {
		k++;
		int i;
		for (i = 0;i < 13;i++) 
			if (nodes[i].color == 0) {
				nodes[i].color = k;
				break;
			}
			
		if (i == 13)
			break;
			
		for (int j = 0;j < 13;j++)
			if (ifOk(nodes,graph,k,j))
				nodes[j].color = k;
	}
	
	//输出所需最小颜色数和一种涂色方案 
	cout <<"所需要的最小颜色数目:"<<k-1<<endl;
	cout<<"一种涂色方案为:"<<endl;
	for(int i=1;i<k;i++){
		cout<<i<<':';
		for(int j=0;j<13;j++)
			if(nodes[j].color==i)
				cout<<nodes[j].index<<' ';
		if(i<k-1)
			cout<<endl;
	}
}

编译运行: 

$ g++ traffic12.cpp
$ a.out 
0 0 0 0 1 1 1 0 0 1 0 0 0
0 0 0 0 0 1 1 1 0 1 1 0 0
0 0 0 0 0 0 0 0 0 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 1 0 0 1 0 0
1 1 0 0 0 0 1 0 0 0 1 1 0
1 1 0 0 0 1 0 0 0 0 1 1 0
0 1 0 0 1 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 1 1 1 0 0 0 0 0 0
0 0 1 0 0 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0
按度数由大到小排序后的结果:1 5 6 10 0 11 2 4 7 9 3 8 12 
度数为5的顶点:1 5 6 10 
度数为4的顶点:0 11 
度数为3的顶点:2 4 7 9 
度数为2的顶点:无
度数为1的顶点:无
度数为0的顶点:3 8 12 
所需要的最小颜色数目:4
一种涂色方案为:
1:1 0 11 3 8 12 
2:5 2 4 
3:6 7 9 
4:10
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部