地图着色问题

地图着色问题

已知中国地图,对各省进行着色,要求相邻省所使用的颜色不同,并保证使用的颜色总数最少。设计对图进行着色的算法,分析该算法的时间空间复杂度,并利用该算法实现对中国地图着色。

问题分析与解决思路

首先将地图区域之间的邻接关系抽象为图上点与点的邻接关系,所以可以地图着色问题可以转换为一个图问题:已知一个图,要求给图上每个点上色,并保证该点的颜色与它的邻接点的颜色都不相同。

我们可以将整个问题划分为更小的子问题:给一个点上色,确保它的颜色与它的邻接点的颜色都不相同。所以可以利用类似广度优先的策略,遍历图上的每一个点,遍历时给该点贪心地选择与邻接点不同的颜色,即总是从低序列开始选择,知道遍历图上的所有点。

算法描述

记点个数为v,边的条数为e。

我们将上述分析过程和解决的思路进一步归纳为以下步骤:

(1)选择一个点为起点加入待遍历队列。

(2)从队列中取出第一个元素,判断它的邻接点的上色情况,贪心地选择一种颜色上色,并将它未上色的邻接点加入待遍历队列。

(3)重复第(2)步直到队列为空。

复杂度分析

使用到的数据结构为队列。

本算法运算时需要对每个点的每个邻接进行判断,为(2×e)次判断操作,及给没上色的点上色,有v次操作,一共(2×e)+v次操作,由于在着色问题当中,边数(所有邻接省份数)比点数(省份数)多,所以时间复杂度可认为是θ(v)。

运算时创建了一个额外的队列来记录待遍历的点,所以空间复杂度为(e)。

运行结果分析

通过分析,发现一般的贪心算法(任意起点)无法确保上色时使用的颜色数量一定是最少的。

原算法的运行情况如下图所示:

上色过程

 

图5.2 上色结果

在查找资料的过程中,我了解到另一种图着色算法:Welch Powell顶点着色算法:

(1)将G的结点按照度数递减的次序排列。

(2)用第一种颜色对第一个结点着色,并按照结点排列的次序。对与前面着色点不邻接的每一点着以相同颜色。(这种排列可能并不是唯一的,可能有相同度数。)

(3)用第二种颜色对尚未着色的点重复步骤(2)。用第三种颜色继续这种作法,直到所有点着色完为止。

可以发现,该算法也利用了贪心策略:度数递减序列和颜色序列。相比于我的算法,该算法实现了进一步优化,使得该贪心策略下,使用的颜色可能会更少。

from copy import deepcopy
import networkx as nx
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False

if __name__ == '__main__':
#从自定义的工具文件中读取中国地图的信息
    from tools import get_adjacency
    from tools import get_position
    position = get_position()
    adjacency = get_adjacency()
    province = list(position.keys())
    G = nx.Graph()
    G.add_nodes_from(province)
    G.add_edges_from(adjacency)
    note_labels = dict()
    for p in province:
        note_labels[p] = p
    fig = plt.figure()
    plt.ion()
    all_colors = ['r','g','b','y','c','m','k']
    colors = dict()
    province = list(G.nodes)
    for p in province:
        colors[p] = 'w'
    start = province[0]
    other_nodes = list()
    other_nodes.append(start)
    while other_nodes:
        p = other_nodes.pop(0)
        color_list = deepcopy(all_colors)
        # 给p上一种邻接点没有用过的颜色
        for n in G[p]:
            #将邻接点加入上色目标
            if colors[n] == 'w' and n not in other_nodes:
                other_nodes.append(n)
            elif colors[n] in color_list:
                color_list.remove(colors[n])
        colors[p] = color_list[0]
        plt.clf()
        values = [colors.get(node) for node in G.nodes()]
        nx.draw_networkx_nodes(G,pos=position,node_color=values,node_size=300, alpha=0.6)
        nx.draw_networkx_edges(G, pos=position)
        nx.draw_networkx_labels(G, position, labels=note_labels, font_size=8)
        plt.pause(0.02)
#检查一共用了多少颜色
    s = set()
    for c in colors.values():
        s.add(c)
    print(len(s), s)
    plt.ioff()
    plt.show()

tools.py:

str = '''
北京  北纬39.55 东经116.24
天津  北纬39.02 东经117.12
上海  北纬31.14 东经121.29
重庆  北纬29.59 东经106.54
台湾  台北  北纬25.03 东经121.30
安徽  北纬31.52 东经117.17
福建  北纬26.05 东经119.18
甘肃  北纬36.04 东经103.51
广东  广州  北纬23.08 东经113.14
广西  南宁  北纬22.48 东经108.19
贵州  贵阳  北纬26.35 东经106.42
海南  海口  北纬20.02 东经110.20
河北  石家庄  北纬38.02 东经114.30
河南  郑州  北纬34.46 东经113.40
黑龙江  哈尔滨  北纬45.44 东经126.36
湖北  武汉  北纬30.35 东经114.17
湖南  长沙  北纬28.12 东经112.59
吉林  长春  北纬43.54 东经125.19
江苏  南京  北纬32.03 东经118.46
江西  南昌  北纬28.40 东经115.55
辽宁  沈阳  北纬41.48 东经123.25
内蒙古  呼和浩特  北纬40.48 东经111.41
宁夏  银川  北纬38.27 东经106.16
青海  西宁  北纬36.38 东经101.48
山东  济南  北纬36.40 东经117.00
山西  太原  北纬37.54 东经112.33
陕西  西安  北纬34.17 东经108.57
四川  成都  北纬30.40 东经104.04
西藏  拉萨  北纬29.39 东经91.08
新疆  乌鲁木齐  北纬43.45 东经87.36
云南  昆明  北纬25.04 东经102.42
浙江  杭州  北纬30.16 东经120.10
'''
adj_dict ={
    '北京': ['河北','天津'],
    '天津': ['北京','河北'],
    '河北': ['北京','天津','山东','河南','山西','内蒙古','辽宁'],
    '山西': ['内蒙古','陕西','河南','河北','北京'],
    '陕西': ['山西','河南','湖北','重庆','四川','甘肃','宁夏','内蒙古'],
    '内蒙古': ['黑龙江','吉林','辽宁','河北','山西','宁夏','甘肃','陕西'],
    '河南': ['河北','湖北','陕西','安徽','山西','山东'],
    '黑龙江': ['内蒙古','吉林'],
    '吉林': ['内蒙古','辽宁'],
    '辽宁': ['内蒙古','吉林','河北'],
    '山东': ['河北','河南','江苏','安徽'],
    '宁夏': ['陕西','内蒙古','甘肃'],
    '甘肃': ['陕西','新疆','青海','四川','宁夏','内蒙古'],
    '青海': ['西藏','新疆','甘肃','四川'],
    '西藏': ['新疆','青海','四川','云南'],
    '新疆': ['西藏','甘肃','青海'],
    '云南': ['西藏','四川','贵州','广西'],
    '湖北': ['陕西','河南','安徽','江西','湖南','重庆'],
    '重庆': ['陕西','贵州','湖北','湖南','四川'],
    '四川': ['重庆','陕西','青海','甘肃','云南','贵州','西藏'],
    '上海': ['浙江','江苏'],
    '广东': ['福建','江西','湖南','广西'],
    '广西': ['广东','海南','贵州','云南'],
    '安徽': ['浙江','江西','河南','山东','江苏','湖北'],
    '贵州': ['湖南','广西','四川','重庆','云南'],
    '海南': ['广东'],
    '湖南': ['江西','重庆','贵州','广东','广西','湖北'],
    '江苏': ['山东','河南','安徽','浙江','上海'],
    '江西': ['浙江','福建','安徽','湖北','湖南','广东'],
    '浙江': ['江西','上海','江苏','福建','安徽'],
    '福建': ['浙江','江西','广东'],
    '台湾': ['福建'],
}

def get_adjacency():
    adj =list()
    for i in adj_dict:
        li = adj_dict[i]
        for j in li:
            if j:
                adj.append((i, j))
    return adj

import re
def get_position():
    txt_lines = str.splitlines()
    pos = dict()
    pat = "(\S+).*北纬(\d+\.\d+).*东经(\d+\.\d+)"

    for tl in txt_lines:
        mat = re.match(pat, tl)
        if mat:
            name = mat.group(1).replace(' ', '')
            n = float(mat.group(2).replace(' ', ''))
            e = float(mat.group(3).replace(' ', ''))
            pos[name] = (e, n)
    return pos

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值