算法设计与分析 求解图的m着色问题-python/java实现

求解图的m着色问题

问题描述

给定无向连通图G=(V, E)和m种不同的颜色,用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的两个顶点着不同颜色,则称这个图是m可着色的。图的m着色问题是对于给定图G和m种颜色,找出所有不同的着色法。

输入描述:第1行有3个正整数n、k和m,表示给定的图G有n个顶点、k条边、m种颜色,顶点的编号为1、2、…、n。在接下来的k行中每行有两个正整数u 、v,表示图G的一条边(u 、v)。

输出描述:程序运行结束时将计算出的不同着色方案数输出。如果不能着色,则程序输出-1。

输入样例

5 8 4
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5

样例输出:

共48种着色方案

第一个着色方案:…

第二个着色方案:…

……

问题分析

  1. 什么是回溯法?

    回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。

在这里插入图片描述

​ 例如:

​ 顶点数n=7,颜色数m=3.按照1,2,3,4,5,6,7顺序构造搜索树.按深度优先策略可以得到第一个着3色方案(图中粗线路径所示)是: 顶点1、3和5着色1; 顶点2和6着色2; 顶点4和7着色3.在<1,2>的路径上只有这一个可行解.在<1,3>的路径上也只有一个可行解:顶点1、3和5着色1;顶点4和7着色2;顶点2和6着色3.其实这个解只是将第一个解中的颜色⒉和颜色3交换而已.根据对称性,在这棵树中只需搜索1/3的空间即可,搜索算法共可得到6个解.

import numpy as np


def same_color(i):
    for j in range(1, n + 1):
        if G[i][j] == 1 and x_is_color[i] == x_is_color[j]:
            return False
    return True


def show():
    print(f'第{count}个着色方案:{x_is_color[1:n + 1]}')


def dfs(node):
    global count
    if node > n:  # 达到叶子结点
        count += 1  # 着色方案数增加1
        show()  # 输出方案
    else:
        for t in range(1, m + 1):
            x_is_color[node] = t
            if same_color(node):  # 可以着色j 进入下一个顶点着色
                dfs(node + 1)
            x_is_color[node] = 0


if __name__ == "__main__":
    count = 0
    n = int(input("请输入顶点数:"))  # n个顶点
    k = int(input("请输入边数:"))  # k条边
    m = int(input("请输入颜色数:"))  # m种颜色

    G = np.zeros((n + 1, n + 1))

    x_is_color = np.zeros(n + 1)  # x[i]表示顶点i的着色

    for i in range(k):
        x, y = list(map(int, input("请输入一组整数用空格隔开:").split(" ")))
        G[x][y] = 1
        G[y][x] = 1

    dfs(1)
    if count > 0:
        print(count)
    else:
        print(-1)

运行结果

在这里插入图片描述
在这里插入图片描述

时间复杂度分析

该算法中的每个顶点试探1 ~ m中着色,共n个顶点,对应的解空间树是一颗m叉树(子集树),算法的时间复杂度为 O ( n m n ) O(nm^n) O(nmn)

java版本

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    int count = 0;
    int n, m, k, G[][], x[];

    public Boolean same_color(int i) {
        for (int j = 1; j <= n; j++) {
            if ((G[i][j] == 1) && (x[i] == x[j])) {
                return false;
            }
        }
        return true;
    }

    public void dfs(int node) {
        if (node > n) {
            count++;
            System.out.println("第" + count + "个着色方案:" + Arrays.toString(x));
        } else {
            for (int i = 1; i <= m; i++) {
                x[node] = i;
                if (same_color(node)) {
                    dfs(node + 1);
                }
                x[node] = 0;
            }
        }
    }
    
    public void start() {
        Scanner input = new Scanner(System.in);
        System.out.print("请输入顶点数:");
        n = input.nextInt();
        System.out.print("请输入边数:");
        k = input.nextInt();
        System.out.print("请输入颜色数:");
        m = input.nextInt();

        G = new int[n + 1][n + 1];
        x = new int[n + 1];

        for (int i = 0; i < k; i++) {
            System.out.print("请输入:");
            int a = input.nextInt();
            int b = input.nextInt();
            G[a][b] = 1;
            G[b][a] = 1;
        }
        dfs(1);
        if (count > 0) {
            System.out.println(count);
        } else {
            System.out.println(-1);
        }

    }

    public static void main(String[] args) {
        Main main = new Main();
        main.start();
    }


}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gouzy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值