算法设计-图着色

一、目的

  1. 正确应用回溯算法求解图着色问题。

  2. 对边界值测试结果。

  3. 正确分析算法时间复杂度。

二、实验内容与设计思想

  1. 设计思路
    (1) 输入图的点数,边数,色数。输入图的每条边。生成图。
    (2) 通过回溯算法求可行的图方案,求得后输出。

  2. 主要数据结构

Graph typedef struct Graph {
	vector<set<int>>G;
	int color[MAXN] = { 0 };
	int e, v, ColorNum;}	

G是图中每个点邻接点的储存容器
color储存图中每个点的着色方案
e是图顶点个数,v是图边个数
ColorNum是图的色数

  1. 主要代码结构

三、实验使用环境

软件:Visual Studio 2022/1/4

平台:win10

四、实验步骤和调试过程

4.1 输入图的基本属性

流程图
输入图的点数,边数和色数。
输入图的边生成模式:
    如果模式=0,则手动输入。
    如果模式=1,则自动输入。
然后输出图结构。
数据测试与结果:
图的点数图的边数图的色数图结构
6183
第 1条边:(5, 4) 第 2条边:(4, 2)
第 3条边:(3, 5) 第 4条边:(3, 1)
第 5条边:(4, 3) 第 6条边:(2, 2)
第 7条边:(3, 1) 第 8条边:(3, 0)
第 9条边:(0, 5) 第10条边:(1, 3)
第11条边:(2, 0) 第12条边:(3, 3)
第13条边:(5, 5) 第14条边:(0, 5)
第15条边:(2, 1) 第16条边:(4, 1)
第17条边:(5, 4) 第18条边:(4, 1)
第19条边:(1, 0) 第20条边:(1, 4)
第21条边:(1, 3) 第22条边:(4, 2)
第23条边:(0, 3)
6
21
5
第 1条边:(5, 4) 第 2条边:(4, 2)
第 3条边:(3, 5) 第 4条边:(3, 1)
第 5条边:(4, 3) 第 6条边:(2, 2)
第 7条边:(3, 1) 第 8条边:(3, 0)
第 9条边:(0, 5) 第10条边:(1, 3)
第11条边:(2, 0) 第12条边:(3, 3)
第13条边:(5, 5) 第14条边:(0, 5)
第15条边:(2, 1) 第16条边:(4, 1)
第17条边:(5, 4) 第18条边:(4, 1)
第19条边:(1, 0) 第20条边:(1, 4)
第21条边:(1, 3)
7
30
6
第 1条边:(6, 6) 第 2条边:(2, 5)
第 3条边:(5, 6) 第 4条边:(0, 4)
第 5条边:(3, 4) 第 6条边:(2, 6)
第 7条边:(3, 2) 第 8条边:(6, 4)
第 9条边:(2, 5) 第10条边:(6, 4)
第11条边:(2, 1) 第12条边:(1, 4)
第13条边:(2, 4) 第14条边:(4, 1)
第15条边:(3, 3) 第16条边:(4, 2)
第17条边:(3, 0) 第18条边:(3, 6)
第19条边:(5, 5) 第20条边:(1, 6)
第21条边:(2, 5) 第22条边:(3, 1)
第23条边:(0, 2) 第24条边:(4, 1)
第25条边:(2, 0) 第26条边:(5, 1)
第27条边:(1, 3) 第28条边:(3, 5)
第29条边:(2, 1) 第30条边:(4, 1)
7
26
4
第 1条边:(6, 6) 第 2条边:(2, 5)
第 3条边:(5, 6) 第 4条边:(0, 4)
第 5条边:(3, 4) 第 6条边:(2, 6)
第 7条边:(3, 2) 第 8条边:(6, 4)
第 9条边:(2, 5) 第10条边:(6, 4)
第11条边:(2, 1) 第12条边:(1, 4)
第13条边:(2, 4) 第14条边:(4, 1)
第15条边:(3, 3) 第16条边:(4, 2)
第17条边:(3, 0) 第18条边:(3, 6)
第19条边:(5, 5) 第20条边:(1, 6)
第21条边:(2, 5) 第22条边:(3, 1)
第23条边:(0, 2) 第24条边:(4, 1)
第25条边:(2, 0) 第26条边:(5, 1)
8
36
5
第 1条边:(5, 6) 第 2条边:(6, 2)
第 3条边:(7, 3) 第 4条边:(5, 7)
第 5条边:(4, 5) 第 6条边:(6, 6)
第 7条边:(7, 1) 第 8条边:(3, 4)
第 9条边:(4, 5) 第10条边:(1, 5)
第11条边:(0, 6) 第12条边:(1, 5)
第13条边:(3, 3) 第14条边:(6, 3)
第15条边:(4, 1) 第16条边:(0, 7)
第17条边:(3, 0) 第18条边:(0, 5)
第19条边:(5, 6) 第20条边:(5, 2)
第21条边:(3, 3) 第22条边:(6, 6)
第23条边:(4, 5) 第24条边:(1, 3)
第25条边:(5, 5) 第26条边:(4, 6)
第27条边:(6, 1) 第28条边:(2, 3)
第29条边:(7, 3) 第30条边:(0, 5)
第31条边:(2, 2) 第32条边:(0, 6)
第33条边:(2, 7) 第34条边:(5, 4)
第35条边:(5, 3) 第36条边:(1, 6)
8
40
5
第 1条边:(5, 6) 第 2条边:(6, 2)
第 3条边:(7, 3) 第 4条边:(5, 7)
第 5条边:(4, 5) 第 6条边:(6, 6)
第 7条边:(7, 1) 第 8条边:(3, 4)
第 9条边:(4, 5) 第10条边:(1, 5)
第11条边:(0, 6) 第12条边:(1, 5)
第13条边:(3, 3) 第14条边:(6, 3)
第15条边:(4, 1) 第16条边:(0, 7)
第17条边:(3, 0) 第18条边:(0, 5)
第19条边:(5, 6) 第20条边:(5, 2)
第21条边:(3, 3) 第22条边:(6, 6)
第23条边:(4, 5) 第24条边:(1, 3)
第25条边:(5, 5) 第26条边:(4, 6)
第27条边:(6, 1) 第28条边:(2, 3)
第29条边:(7, 3) 第30条边:(0, 5)
第31条边:(2, 2) 第32条边:(0, 6)
第33条边:(2, 7) 第34条边:(5, 4)
第35条边:(5, 3) 第36条边:(1, 6)
第37条边:(6, 7) 第38条边:(4, 5)
第39条边:(6, 5) 第40条边:(6, 6)
……
……
……
……

4.2 回溯算法

流程图

搜索过程、回溯算法过程:

①初始化颜色总数为点数e

②每次从点集中选择一个顶点并从第一种颜色开始尝试对其进行着色;

③如果着色不与邻接点冲突,则继续通过相同的方式处理点集中的下一个顶点;如果着色冲突(与邻接点颜色一样),则说明该种着色方法行不通,则尝试另一个颜色,知道色数已经达顶,则退回到上一个结点,将上一个结点的着色改为当前着色的下一种颜色。

④重复上述过程,直到所有的顶点都着色为止,此时确定了一个可行解。通过Graph的display函数打印出来。

⑤得到一个可行解后进行回溯,退回到上一个着色颜色序号小于当前颜色总数的结点上,将其着色改为下一种,并进行如上所示的推理过程。

⑥当存在一个结点没有颜色可以着色时,算法停止。

剪枝过程:

遍历所有点的邻接点,如果邻接点的个数>=图的色数,则不会有可行解,省去探索过程。

测试数据与结果:
图的点数图的边数图的色数解法
5103
第 1种解法: 1 1 1 2 2
第 2种解法: 1 1 1 2 3
第 3种解法: 1 1 1 3 2
第 4种解法: 1 1 1 3 3
第 5种解法: 1 1 2 2 3
第 6种解法: 1 1 2 3 3
第 7种解法: 1 1 3 2 2
第 8种解法: 1 1 3 3 2
第 9种解法: 1 2 1 3 3
第 10种解法: 1 2 2 3 3
第 11种解法: 1 3 1 2 2
第 12种解法: 1 3 3 2 2
第 13种解法: 2 1 1 3 3
第 14种解法: 2 1 2 3 3
第 15种解法: 2 2 1 1 3
第 16种解法: 2 2 1 3 3
第 17种解法: 2 2 2 1 1
第 18种解法: 2 2 2 1 3
第 19种解法: 2 2 2 3 1
第 20种解法: 2 2 2 3 3
第 21种解法: 2 2 3 1 1
第 22种解法: 2 2 3 3 1
第 23种解法: 2 3 2 1 1
5103无可行方案!
6153
第 1种解法: 1 2 2 1 1 3
第 2种解法: 1 2 2 1 2 3
第 3种解法: 1 2 3 1 1 2
第 4种解法: 1 2 3 1 3 2
第 5种解法: 1 3 2 1 1 3
第 6种解法: 1 3 2 1 2 3
第 7种解法: 1 3 3 1 1 2
第 8种解法: 1 3 3 1 3 2
第 9种解法: 2 1 1 2 1 3
第 10种解法: 2 1 1 2 2 3
第 11种解法: 2 1 3 2 2 1
第 12种解法: 2 1 3 2 3 1
第 13种解法: 2 3 1 2 1 3
第 14种解法: 2 3 1 2 2 3
第 15种解法: 2 3 3 2 2 1
第 16种解法: 2 3 3 2 3 1
第 17种解法: 3 1 1 3 1 2
第 18种解法: 3 1 1 3 3 2
第 19种解法: 3 1 2 3 2 1
第 20种解法: 3 1 2 3 3 1
第 21种解法: 3 2 1 3 1 2
第 22种解法: 3 2 1 3 3 2
第 23种解法: 3 2 2 3 2 1
第 24种解法: 3 2 2 3 3 1
7303无可行方案!
7404
第 1种解法: 1 2 3 4 3 1 2
第 2种解法: 1 2 4 3 4 1 2
第 3种解法: 1 3 2 4 2 1 3
第 4种解法: 1 3 4 2 4 1 3
第 5种解法: 1 4 2 3 2 1 4
第 6种解法: 1 4 3 2 3 1 4
第 7种解法: 2 1 3 4 3 2 1
第 8种解法: 2 1 4 3 4 2 1
第 9种解法: 2 3 1 4 1 2 3
第 10种解法: 2 3 4 1 4 2 3
第 11种解法: 2 4 1 3 1 2 4
第 12种解法: 2 4 3 1 3 2 4
第 13种解法: 3 1 2 4 2 3 1
第 14种解法: 3 1 4 2 4 3 1
第 15种解法: 3 2 1 4 1 3 2
第 16种解法: 3 2 4 1 4 3 2
第 17种解法: 3 4 1 2 1 3 4
第 18种解法: 3 4 2 1 2 3 4
第 19种解法: 4 1 2 3 2 4 1
第 20种解法: 4 1 3 2 3 4 1
第 21种解法: 4 2 1 3 1 4 2
第 22种解法: 4 2 3 1 3 4 2
第 23种解法: 4 3 1 2 1 4 3
第 24种解法: 4 3 2 1 2 4 3
……………………

4.4 时间复杂度

  • 生成图:O(2v)
    v------图的边数
    无好坏情况之分,图结构是由点和边组成的,e是点数,但是e不用生成,是从0-e排列的,v是边数,因为是随机数生成,所以生成v条边,需要2v的时间。

  • 剪枝:O(1)~O(e)
    e------图的点数
    遍历图的点,如果图的任意一个点的邻接点个数>=色数,则说明图无可行解。

    • 最好的情况:
      O(1)------如果遍历第一个点的时候,就发现第一个点的邻接点个数>=色数,则结束。
    • 最差的情况:
      O(e)------如果遍历最后一个点的时候,才发现无可行解。或者所有点的邻接点个数都<色数,则需要时间O(e)。
  • 单次探索:O(n+k)
    n------该点的邻接点个数
    k------该点的可行着色数量
    n:遍历邻接点,将他们的颜色都标注。
    k:遍历色数,当目前颜色没有被标注,则该点染这个颜色,并进行下一个递归。所以染色和递归的次数取决于该点的可行着色数量,即该点所有邻接点中小标小于该点的数量。

  • 整个探索过程:O( f ( 1 )   × f ( 2 ) × … × f ( e ) f(1)\ \times f(2) \times \ldots \times f(e) f(1) ×f(2)××f(e))
    f ( i n d e x ) f(index) f(index):是下标为index的点的可着色数量。即m-(index的邻接点中已经染色数量)。
    且已知 [ m − f ( 1 ) ] + [ m − f ( 2 ) ] + … + [ m − f ( e ) ] = v \left\lbrack m - f(1) \right\rbrack + \left\lbrack m - f(2) \right\rbrack + \ldots + \left\lbrack m - f(e) \right\rbrack = v [mf(1)]+[mf(2)]++[mf(e)]=v,即 e ⋅ m − ∑ i = 1 e f ( i ) = v e \cdot m - \sum_{i = 1}^{e}{f(i)} = v emi=1ef(i)=v

综上:
最好情况:O(1)
最差情况:O( f ( 1 )   × f ( 2 ) × … × f ( e ) f(1)\ \times f(2) \times \ldots \times f(e) f(1) ×f(2)××f(e))

700次图着色时间图

4.6 空间复杂度

  • 生成图:O(2v+e)
    v------边的个数
    e------点的个数
    图的储存形式是点储存,即对每个点储存它的邻接点。所有点的邻接点加起来是2v个。
    图结构中还有一个color数组用来储存每个点的颜色,长度为e。
  • 探索过程:O(m+1)
    m------图的色数
    需要一个数组color记录每个色数的使用情况,如果邻接点中有颜色已经被使用,则该颜色会在color中被标注。该color数组的长度即m。

五、附录

图着色
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3;
int ansNum = 0;

typedef struct Graph {
	vector<set<int>>G;
	int color[MAXN] = { 0 };
	int e, v, ColorNum;

	Graph(int e, int v, int ColorNum) {
		this->e = e;
		this->v = v;
		this->ColorNum = ColorNum;
		for (int i = 0; i < e; i++)
		{
			G.push_back(set<int>());
		}
		srand((int)time(NULL));
	}

	void CreatV()
	{
		for (int i = 0; i < v; i++)
		{
			int a = rand() % e, b = rand() % e;
			printf("第%2d条边 (%d, %d) \n", i + 1, a, b);
			if (a == b)
				continue;
			G[a].insert(b);
			G[b].insert(a);
		}
		printf("--------------------------------------------\n");
	}

	void display()
	{
		printf("第%3d种解法:", ++ansNum);
		for (int i = 1; i < e; i++)
		{
			printf("%2d ", color[i]);
		}
		printf("\n");
	}
}Graph;

Graph Input()
{
	int e, v, m, mode;
	printf("请输入图的点数——");
	scanf("%d", &e);
	printf("请输入图的边数——");
	scanf("%d", &v);
	printf("请输入颜色数量——");
	scanf("%d", &m);
	Graph graph = Graph(e, v, m);

	printf("请选择边的产生形式:[0]手动输入 [1]自动输入——");
	mode = 1;
	//scanf("%d", &mode);
	printf("\n\n--------------------------------------------\n");
	if (mode)
		graph.CreatV();
	else
	{
		for (int i = 0; i < v; i++)
		{
			int a, b;
			scanf("%d %d", &a, &b);
			printf("第%2d条边 (%d, %d) \n", i + 1, a, b);
			if (a == b)
				continue;
			graph.G[a].insert(b);
			graph.G[b].insert(a);
		}
		printf("--------------------------------------------\n");
	}
	return graph;
}
void solve(int index, Graph graph)
{
	int e = graph.e;
	if (index == e)
	{
		graph.display();
		return;
	}
	vector<int>color(graph.ColorNum + 1, 0);
	for (auto neighbor : graph.G[index])
	{
		int neighbor_color = graph.color[neighbor];
		color[neighbor_color] = 1;
	}
	for (int i = 1; i <= graph.ColorNum; i++)
	{
		if (!color[i])
		{
			graph.color[index] = i;
			solve(index + 1, graph);
			graph.color[index] = 0;
		}
	}
}
int main()
{
	Graph graph = Input();
	solve(0, graph);
	if (ansNum == 0)
		printf("无可行方案!");
}
打印图
#include<bits/stdc++.h>
#define random(a,b) (rand()%(b-a)+a)
using namespace std;
const int MAXN = 1e3;
int ansNum = 0;

void writeFile(string);


typedef struct Graph {
	vector<set<int>>G;
	int color[MAXN] = { 0 };
	int e, v, ColorNum;

	Graph(int e, int v, int ColorNum) {
		this->e = e;
		this->v = v;
		this->ColorNum = ColorNum;
		for (int i = 0; i < e; i++)
		{
			G.push_back(set<int>());
		}
		srand((int)time(NULL));
	}

	void CreatV()
	{
		string str = "";
		for (int i = 0; i < v; i++)
		{
			int a = rand() % e, b = rand() % e;
			printf("第%2d条边:(%d, %d)    ", i + 1, a, b);
			if (i & 1)
				printf("\n");
			if (a == b)
				continue;
			G[a].insert(b);
			G[b].insert(a);
		}
		printf("\n\n");
	}
}Graph;

int main()
{
	srand((int)time(0));
	for (int e = 6; e < 10; e++)
	{
		for (int i = 0; i < 2; i++)
		{
			int v = random((int)(e * e / 2), (int)(e * e / 1.5));
			int m = random((int)(e / 2.5), e);
			ansNum = 0;

			string ss = "点数 = " + to_string(e) + "; 边数 = " + to_string(v) + "; 色数 = " + to_string(m) + ";\n";
			cout << ss;
			Graph graph = Graph(e, v, m);
			graph.CreatV();
		}
	}
}

时间散点图代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import random


def draw(x, y, z):
    plt.style.use('seaborn-whitegrid')
    colors = [random.random() for i in range(len(x))]
    ax = plt.subplot(projection='3d')  # 创建一个三维的绘图工程
    ax.set_title('Times of Graph coloring')  # 设置本图名称
    ax.scatter(x, y, z, s=30, c=colors, cmap='viridis')

    ax.set_xlabel('e')  # 设置x坐标轴
    ax.set_ylabel('v')  # 设置y坐标轴
    ax.set_zlabel('Times')  # 设置z坐标轴

    plt.tick_params(pad=1)  # 刻度距离坐标轴的距离调整
    plt.savefig(r"D:\Desktop\a.png", bbox_inches='tight', dpi=1000)
    plt.show()


if __name__ == "__main__":
    path = r"D:\Desktop\time.txt"
    data = open(path).readlines()
    x, y, z = [], [], []
    for i in data:
        a, b, c = i.split(' ')
        x.append(int(a))
        y.append(int(b))
        z.append(float(c))
    draw(x, y, z)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值