日撸Java三百行(day35:图的m着色问题)

news2024/9/25 20:19:04

目录

一、问题描述

二、思路分析

三、代码实现

总结


一、问题描述

在高中学习排列组合的时候,有一个非常经典的问题,就是涂色问题,即用m种颜色给n块区域涂色,要求每块区域只能涂同一种颜色且相邻区域的颜色不能相同,问一共有多少种涂色方案。还记得当时自己是怎么做的吗?下面我们就用一个具体的例子来回顾一下。

如下图,共有A、B、C、D四块区域,用五种颜色给它们涂色,要求每块区域只能涂同一种颜色且相邻区域的颜色不能相同,请问一共有多少种涂色方案?

采用枚举法,第一步,给A区域涂色,由于A区域是第一个涂色的区域,没有任何颜色限制,所以有5种方法; 第二步,给B区域涂色,B区域与A区域相邻,使得B区域不能涂A区域涂过的颜色,所以共有4种方法;第三步,给C区域涂色,C区域与B区域相邻,使得C区域不能涂B区域涂过的颜色,所以共有4种方法;第四步,给D区域涂色,D区域与B、C区域都相邻,使得D区域不能涂B、C区域涂过的颜色,所以共有3种方法。最后,根据分步乘法计数原理,得到共有5*4*4*3=240种方案。

如果这只是高中的一道数学题,那必然不会放到这里来说,所以接下来我们就要将它抽象成图。显然,一块一块的区域可以看作图的一个个节点,因此给区域涂色就是给节点涂色,要求相邻区域的颜色不能相同就是要求邻接节点的颜色不能相同,所以上述例子就可以改写如下(区域与区域相邻显然是一个双向相邻,所以这里我们需要用到的是无向图):

 这也就是今天我们要讨论的问题——图的m着色问题。

二、思路分析

那么该如何来解决这个问题呢?对于排列组合给区域涂色的问题,我们使用的是枚举法,同理,图的m着色问题我们同样可以使用枚举法(穷举法)来解决,也就是使用暴力解题法来完成。仍然以上图为例,进行具体说明:

  • 将A、B、C、D四个节点编号为0、1、2、3号节点,将五种颜色编号为0、1、2、3、4号颜色,然后开始涂色。
  • 假设从0号节点开始涂色0号颜色,那么1号节点可以涂1、2、3、4号颜色。
  • 如果1号节点涂色1号颜色,那么2号节点可以涂色0、2、3、4号颜色;如果1号节点涂色2号颜色,那么2号节点可以涂色0、1、3、4号颜色;如果1号节点涂色3号颜色,那么2号节点可以涂色0、1、2、4号颜色;如果1号节点涂色4号颜色,那么2号节点可以涂色0、1、2、3号颜色。
  • 如果1号节点涂色1号颜色,2号节点涂色0号颜色,那么3号节点可以涂色2、3、4号颜色;如果1号节点涂色1号颜色,2号节点涂色2号颜色,那么3号节点可以涂色0、3、4号颜色;如果1号节点涂色1号颜色,2号节点涂色3号颜色,那么3号节点可以涂色0、2、4号颜色……

以上就是这个问题的暴力解题法。

接下来,我们思考如何用代码来实现。如下图,仍然对节点和颜色分别进行从0开始的编号,并以节点总数为长度设置一个颜色标记数组,这样一来颜色标记数组的下标就与节点的编号达成了一致;颜色标记数组中的具体元素使用颜色编号来填充,这样通过数组下标就可以知道几号节点涂色了几号颜色;再设置一个默认初始值-1用于表示节点还未被涂色。

然后,我们从左往右(即从下标为0的节点开始)对颜色标记数组中的数据元素进行枚举,也就是用颜色编号进行填充,注意邻接节点在颜色标记数组中对应的位置不能存放相同的颜色编号。

我们暂时就分析到这里,剩下的内容在下面的代码实现过程中再继续。

三、代码实现

首先,进行初始化,如下:

    /**
	 *********************
	 * Coloring. Output all possible schemes.
	 * 
	 * @param paraNumColors The number of colors.
	 *********************
	 */
	public void coloring(int paraNumColors) {
		// Step 1. Initialize.
		int tempNumNodes = connectivityMatrix.getRows();
		int[] tempColorScheme = new int[tempNumNodes];
		Arrays.fill(tempColorScheme, -1);

同样利用connectivityMatrix.getRows()获得节点总数tempNumNodes,然后将其作为数组长度创建一个int类型的颜色标记数组tempColorScheme,最后利用Array.fill()方法对tempColorScheme填充默认初始值-1。

补充:

Array.fill(数组名,默认初始值)方法:用于对一个数组快速填充同一默认初始值

然后,创建关键方法(其实也是对上面coloring方法的一个重载),如下:

    /**
	 *********************
	 * Coloring. Output all possible schemes.
	 * 
	 * @param paraNumColors The number of colors.
	 * @param paraCurrentNumNodes The number of nodes that have been colored.
	 * @param paraCurrentColoring The array recording the coloring scheme.
	 *********************
	 */
	public void coloring(int paraNumColors, int paraCurrentNumNodes, int[] paraCurrentColoring) {
		// Step 1. Initialize.
		int tempNumNodes = connectivityMatrix.getRows();

		System.out.println("coloring: paraNumColors = " + paraNumColors + ", paraCurrentNumNodes = "
				+ paraCurrentNumNodes + ", paraCurrentColoring" + Arrays.toString(paraCurrentColoring));
		// A complete scheme.
		if (paraCurrentNumNodes >= tempNumNodes) {
			System.out.println("Find one:" + Arrays.toString(paraCurrentColoring));
			return;
		} // Of if

		// Try all possible colors.
		for (int i = 0; i < paraNumColors; i++) {
			paraCurrentColoring[paraCurrentNumNodes] = i;
			if (!colorConflict(paraCurrentNumNodes + 1, paraCurrentColoring)) {
				coloring(paraNumColors, paraCurrentNumNodes + 1, paraCurrentColoring);
			} // Of if
		} // Of for i
	} // Of coloring

该方法输入了三个参数,其中paraNumColors表示一共有几种颜色,paraCurrentNumNodes表示当前涂色节点的编号(即当前涂色节点在颜色标记数组中的对应下标),paraCurrentColoring表示当前的颜色标记数组。然后,通过一条输出语句将此时三个参数的值进行输出。

在程序运行过程中,逐步向颜色标记数组输入颜色编号,当paraCurrentNumNodes >= tempNumNodes即当前涂色节点的编号 >= 节点总数时,说明所有的节点均已完成了一次涂色,也就是说找到了一种涂色方案,此时直接输出结果(即输出当前的颜色标记数组)。

当paraCurrentNumNodes没有大于等于tempNumNodes即当前涂色节点的编号没有大于等于节点总数时,则进入循环,对节点进行涂色。在for循环中paraCurrentColoring[paraCurrentNumNodes] = i 表示将 i 号颜色的编号 i 输入当前涂色节点在颜色标记数组中的对应位置,相当于给当前涂色节点涂上 i 号颜色;接着,借助一个if语句,使得当涂色不冲突时继续给编号加1的节点(相当于在颜色标记数组中向右移动一格)进行涂色,而当涂色冲突时则给当前涂色节点涂上 i + 1 号颜色后,再次进行涂色冲突判断。

创建一个判断涂色是否冲突的方法,如下:

    /**
	 *********************
	 * Coloring conflict or not. Only compare the current last node with previous
	 * ones.
	 * 
	 * @param paraCurrentNumNodes The current number of nodes.
	 * @param paraColoring        The current coloring scheme.
	 * @return Conflict or not.
	 *********************
	 */
	public boolean colorConflict(int paraCurrentNumNodes, int[] paraColoring) {
		for (int i = 0; i < paraCurrentNumNodes - 1; i++) {
			// No direct connection.
			if (connectivityMatrix.getValue(paraCurrentNumNodes - 1, i) == 0) {
				continue;
			} // Of if

			if (paraColoring[paraCurrentNumNodes - 1] == paraColoring[i]) {
				return true;
			} // Of if
		} // Of for i
		return false;
	} // Of colorConflict

connectivityMatrix.getValue()调用了之前整数矩阵类IntMatrix的getValue()方法,用于获得整数矩阵对象connectivityMatrix的某个具体元素值;然后利用了我们之前进行图的连通性检测的结论,即如果连通矩阵中某个元素的值为0,那么该元素行标对应的节点到该元素列标对应的节点不连通,不连通必然不邻接,也就不会发生涂色冲突,所以直接continue结束本次循环,返回false,代表涂色不冲突;但是如果paraColoring[paraCurrentNumNodes - 1] = paraColoring[ i ],则说明涂色会发生冲突,于是返回true。

最后,设置一个单元测试,如下:

    /**
	 *********************
	 * Coloring test.
	 *********************
	 */
	public static void coloringTest() {
		int[][] tempMatrix = { { 0, 1, 1, 0 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 }, { 0, 1, 0, 0 } };
		Graph tempGraph = new Graph(tempMatrix);
		// tempGraph.coloring(2);
		tempGraph.coloring(3);
	} // Of coloringTest

完整的程序代码:

    /**
	 *********************
	 * Coloring. Output all possible schemes.
	 * 
	 * @param paraNumColors The number of colors.
	 *********************
	 */
	public void coloring(int paraNumColors) {
		// Step 1. Initialize.
		int tempNumNodes = connectivityMatrix.getRows();
		int[] tempColorScheme = new int[tempNumNodes];
		Arrays.fill(tempColorScheme, -1);

		coloring(paraNumColors, 0, tempColorScheme);
	} // Of coloring

	/**
	 *********************
	 * Coloring. Output all possible schemes.
	 * 
	 * @param paraNumColors The number of colors.
	 * @param paraCurrentNumNodes The number of nodes that have been colored.
	 * @param paraCurrentColoring The array recording the coloring scheme.
	 *********************
	 */
	public void coloring(int paraNumColors, int paraCurrentNumNodes, int[] paraCurrentColoring) {
		// Step 1. Initialize.
		int tempNumNodes = connectivityMatrix.getRows();

		System.out.println("coloring: paraNumColors = " + paraNumColors + ", paraCurrentNumNodes = "
				+ paraCurrentNumNodes + ", paraCurrentColoring" + Arrays.toString(paraCurrentColoring));
		// A complete scheme.
		if (paraCurrentNumNodes >= tempNumNodes) {
			System.out.println("Find one:" + Arrays.toString(paraCurrentColoring));
			return;
		} // Of if

		// Try all possible colors.
		for (int i = 0; i < paraNumColors; i++) {
			paraCurrentColoring[paraCurrentNumNodes] = i;
			if (!colorConflict(paraCurrentNumNodes + 1, paraCurrentColoring)) {
				coloring(paraNumColors, paraCurrentNumNodes + 1, paraCurrentColoring);
			} // Of if
		} // Of for i
	} // Of coloring

	/**
	 *********************
	 * Coloring conflict or not. Only compare the current last node with previous
	 * ones.
	 * 
	 * @param paraCurrentNumNodes The current number of nodes.
	 * @param paraColoring        The current coloring scheme.
	 * @return Conflict or not.
	 *********************
	 */
	public boolean colorConflict(int paraCurrentNumNodes, int[] paraColoring) {
		for (int i = 0; i < paraCurrentNumNodes - 1; i++) {
			// No direct connection.
			if (connectivityMatrix.getValue(paraCurrentNumNodes - 1, i) == 0) {
				continue;
			} // Of if

			if (paraColoring[paraCurrentNumNodes - 1] == paraColoring[i]) {
				return true;
			} // Of if
		} // Of for i
		return false;
	} // Of colorConflict

	/**
	 *********************
	 * Coloring test.
	 *********************
	 */
	public static void coloringTest() {
		int[][] tempMatrix = { { 0, 1, 1, 0 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 }, { 0, 1, 0, 0 } };
		Graph tempGraph = new Graph(tempMatrix);
		// tempGraph.coloring(2);
		tempGraph.coloring(3);
	} // Of coloringTest

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		System.out.println("Hello!");
		Graph tempGraph = new Graph(3);
		System.out.println(tempGraph);

		// Unit test.
		getConnectivityTest();

		breadthFirstTraversalTest();

		depthFirstTraversalTest();

		coloringTest();
	} // Of main

部分运行结果:

总结

对于图的m着色问题,我们使用的是枚举法,也是一种暴力解题法。对于人来说,枚举法似乎看起来是一种“笨方法”,因为它没有特别高的技术含量而且还很繁琐,但是对于计算机而言则不然,暴力解题法的逻辑相对简单直接,利用计算机比较容易实现,而且当规模不是很多的时候,暴力解题法可能比复杂的优化算法更为高效。总之,学习计算机万能的暴力解题法是必不可少的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2077508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pyinstaller将python程序打包成exe文件

将python代码打包成exe文件可以在不安装python环境的情况下直接运行python代码&#xff0c;譬如自己在自己的电脑上写好了代码&#xff0c;想发给其他人使用&#xff0c;可以用下述方法将python程序打包成exe文件&#xff0c;其他人直接执行exe文件即可使用该程序。 1.安装pyi…

二叉搜索树:数据结构之美

目录 引言基础知识 定义性质操作详解 插入节点删除节点查找节点遍历 前序遍历中序遍历后序遍历高级主题 平衡问题AVL树简介应用案例总结 引言 二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树&#xff0c;它的每个节点具有以下性质&#xff1a;左子树上的所有节点的键…

Python数据采集与网络爬虫技术实训室解决方案

在大数据与人工智能时代&#xff0c;数据采集与分析已成为企业决策、市场洞察、产品创新等领域不可或缺的一环。而Python&#xff0c;作为一门高效、易学的编程语言&#xff0c;凭借其强大的库支持和广泛的应用场景&#xff0c;在数据采集与网络爬虫领域展现出了非凡的潜力。唯…

Mysql重要参数

1、是否开启慢SQL日志 show VARIABLES like slow_query_log%; 2、慢SQL日志保存位置 show VARIABLES like slow_query_log_file%; 3、慢SQL的阈值&#xff0c;超过则是慢SQL&#xff0c;单位秒&#xff0c;默认10s show VARIABLES like long_query_time%;

小阿轩yx-Kubernetes存储入门

小阿轩yx-Kubernetes存储入门 前言 数据是一个企业的发展核心&#xff0c;它涉及到数据存储和数据交换的内容。在生产环境中尤为重要的一部分在 Kubernetes 中另一个重要的概念就是数据持久化 Volume。 Volume 的概念 对多数项目而言 数据文件的存储是非常常见的 在 Kube…

计算机的错误计算(七十四 )

摘要 回复网友的疑问&#xff1a;用错数解释计算机的错误计算&#xff08;六十四&#xff09;中的错误计算原因。 计算机的错误计算&#xff08;六十四&#xff09;到&#xff08;六十九&#xff09;&#xff0c;以及&#xff08;七十一&#xff09;与&#xff08;七十三&…

攻防世界 1000次点击

做题笔记。 下载解压 查壳。 32位ida打开。 查找字符串。 winmain函数写的&#xff0c;程序运行如下&#xff1a; 一开始思路是想着分析找到关键代码然后去od进行调试。 后来&#xff0c;额&#xff0c;不想看代码了。吐了。 尝试去字符串搜索flag样式&#xff0c;确实一发现…

高效恢复,无忧存储:2024年数据恢复工具大搜罗

不知道你是否了解过电子存储设备&#xff0c;我们的设备往往都存储在一个小小的芯片里&#xff0c;它为我们提供了数据携带的便捷性&#xff0c;当然也为我们带来了数据意外丢失的风险。为了我们的数据安全&#xff0c;我们来探讨一下有什么数据恢复工具能为我们的资料保驾护航…

Ruo-Yi 前后端分离如何不使用注解@DataSource的方式而是使用Mybatis插件技术实现多数据源的切换【可以根据配置文件进行开启/关闭】

Ruo-Yi 前后端分离如何不使用注解DataSource的方式而是使用Mybatis插件技术实现多数据源的切换【可以根据配置文件进行开启/关闭】 1、首先 配置文件&#xff1a; # 数据源配置 spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.c…

ZooKeeper--基于Kubernetes部署ZooKeeper

ZooKeeper 服务 服务类型: 无头服务&#xff08;clusterIP: None&#xff09;&#xff0c;这是 StatefulSet&#xff08;有状态集&#xff09;必需的配置。 端口: 2181 (客户端): 用于客户端连接。 2888 (跟随者): 用于 ZooKeeper 服务器之间的连接。 3888 (领导者): 用于领导者…

邮政快递批量查询解决方案:提升业务运营效率

邮政快递批量查询&#xff1a;固乔快递查询助手的高效体验 在电商行业日益繁荣的今天&#xff0c;快递物流成为了连接商家与消费者的关键纽带。而对于需要处理大量订单的电商企业或个人而言&#xff0c;如何高效、准确地查询和跟踪快递物流信息显得尤为重要。幸运的是&#xf…

linux 云主机下载压缩包安装配置 maven 实录(华为云 EulerOS)

本想通过 yum install maven 直接安装的, 方便省事, 但报错说没找到, 于是只能手动安装了, 把整个过程记录了一下, 包括下载, 解压, 配置及验证的全过程, 并对用到的命令及参数作了详细说明, 需要的同学可以参考. maven 官网找到下载链接 首先要去到 maven 的官网, https://m…

OpenCV+Python自动填涂机读卡

接上一篇OpenCVPython识别机读卡-CSDN博客&#xff0c;既然可以识别机读卡填涂答案了&#xff0c;将标准答案绘制到机读卡上也就简单了。 工作原理 1.答题区域为整张图片最大轮廓&#xff0c;先找出答题区域。 2.答题区域分为6行&#xff0c;每行4组&#xff0c;第6行只有1组…

【Java设计模式】抽象文档模式:以灵活性简化数据处理

文章目录 抽象文档设计模式的意图抽象文档模式的详细解释及实际示例Java中抽象文档模式的编程示例抽象文档模式类图Java中何时使用抽象文档模式抽象文档模式的优点和权衡源码下载参考和致谢 抽象文档设计模式的意图 Java中的抽象文档设计模式是一种关键的结构设计模式&#xf…

【mysql集群之组复制】

目录 一、 mysql高可用之组复制 (MGR)组复制单主和多主模式实现mysql的组复制 二、 mysql-router&#xff08;mysql路由&#xff09;实现负载均衡 一、 mysql高可用之组复制 (MGR) MySQL Group Replication(简称 MGR )是 MySQL 官方于 2016 年 12 月推出的一个全新的高可用与高…

OpenHarmony南向开发:SmartPerf-Device使用说明

简介 SmartPerf 端是一款基于 OpenHarmony 系统开发的性能功耗测试工具&#xff0c;操作简单易用&#xff0c;可提供包括性能、功耗的关键 KPI 指标&#xff0c;给出具体指标的测试值&#xff0c;包括采集设备的 FPS、CPU、GPU、Ftrace 等指标数据&#xff1b; 目前 SmartPer…

uniapp之app版本更新,整体更新和热更新

目录 需求&#xff1a; 版本更新有两种更新模式&#xff1a; 实现&#xff1a; 前提&#xff1a; 热更新&#xff1a; 打包wgt包&#xff1a;菜单->发行->原生App-制作移动App资源升级包 代码逻辑: 整体更新&#xff1a; 实际项目开发&#xff1a; 需求&#xf…

Linux网络编程——C/C++Web服务器(二):IO多路复用select/poll/epoll实现服务器监听多客户端事件

环境配置&#xff1a;windows电脑用户可以安装WSL配置Linux环境&#xff0c;并且安装vscode及wsl的插件通过vscode连接本机电脑的Linux。 前置内容&#xff1a; Linux网络编程——C/CWeb服务器&#xff08;一&#xff09;&#xff1a;不断创建新线程处理多客户端连接和通信-C…

代码随想录算法训练营第二十七天(贪心 一)

硬拖拖到现在才写完。。。 关于贪心: 文章链接: 代码随想录 文章摘要: 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心算法并没有固定的套路。 和其他算法不同&#xff0c;贪心没有能看出局部最优是否能推出整体最优的通法。 用来验证可不可以…

软件渗透测试必要性简析,第三方软件测试机构如何进行渗透测试?

在信息技术迅速发展的今天&#xff0c;软件渗透测试逐渐成为了确保信息安全的重要环节。软件渗透测试指的是对系统或应用程序进行模拟攻击&#xff0c;以发现其潜在的安全风险与脆弱性。不同于传统的安全审计&#xff0c;渗透测试更注重实际攻击过程和攻击者的视角&#xff0c;…