Misra-Gries Edge Coloring(边着色问题)

用户 超几何冰精 的头像
超几何冰精
2025 年 11 月 20 日 更新

问题描述

给出一个一般图,现在将它的每条边染色,使得任意两条相邻(有公共顶点)的边颜色不同。​请给出一种染色方案,使得用到的颜色数量不超过Δ(G)+1

解决方案

试想,现在我们碰到了一条新的边(u, v),它还没被染色。我们想把它染成一个v没有见过的颜色A。不幸的是:u见过这个颜色了——(u, v_0)被染成颜色A了。。。 uv说:要不我们俩先染成颜色A吧,我让v_0换个颜色染。v说:好呀! 那现在(u, v_0)想被染成v_0没有见过的颜色B。不幸的是:u见过颜色B了——(u, v_1)是颜色B。u如法炮制,先跟v_0染着颜色B,再跟v_1另请高明。 如此转转不已,总能找到一个v_l,要么新的颜色uv_l都没见过,要么它见过某颜色,但是邻居在之前的人们里,这样这个过程不得不停下来了。 如果是前者,那非常好:每对顶点的颜色挪一下,最终让(u, v_l)涂上新的没见过的颜色就行啦! 如果是后者:我们还有一个办法:找到在之前人们里的那个邻居,让他和u连接的颜色d先变成全新的没见过的颜色c,再找到一个能接受颜色d的顶点v_w,在前面仍然挪一下,最后给(u, v_w)涂上颜色d 于是,我们的思路就有了!

困难之处

我们称一个可以挪动的序列为一个扇,我们想要找到极大的扇来挪动。但是,找极大扇的过程在朴素想法下是O(n^2)的!于是我们需要精巧的数据结构构造: 考虑用数组存储每个顶点没见过的颜色,每次取数组头的颜色作为下一个人尝试的新颜色。毕竟我们不需要考虑颜色的顺序,这个取颜色的过程可以是O(1)的。 我们还可以记录每个点由某个颜色连接到的顶点,这样无论是考察一个点是否用过某个颜色还是寻找新顶点都可以在O(1)的时间下完成! 遍历边涂色,每个边最坏要把所有点加进扇,所以最终复杂度是O(mn)的。

代码示例

这是我写的垃圾代码,仅供参考。。。

#include <algorithm>
#include <cstdio>
#include <unordered_map>
#include <vector>
#include <numeric>

using std::hash;
using std::iota;
using std::make_pair;
using std::max;
using std::pair;
using std::swap;
using std::unordered_map;
using std::vector;

struct PairHash
{
    size_t operator()(const pair<int, int> &p) const noexcept
    {
        return hash<int>()(p.first) ^ (hash<int>()(p.second) << 1);
    }
};

void popHead(vector<int> &vec)
{
    if (vec.empty())
    {
        return;
    }
    vec[0] = vec.back();
    vec.pop_back();
    return;
}

void removeValue(vector<int> &vec, int x)
{
    for (int i = 0; i < vec.size(); ++i)
    {
        if (vec[i] == x)
        {
            vec[i] = vec.back();
            vec.pop_back();
            return;
        }
    }
    return;
}

void modifyValue(vector<int> &vec, int x, int y)
{
    for (int i = 0; i < vec.size(); ++i)
    {
        if (vec[i] == x)
        {
            vec[i] = y;
            return;
        }
    }
    return;
}

class Graph
{
public:
    int size, maxDegree;

    struct Edge
    {
        int from, to;
    };

    vector<Edge> E;
    vector<int> degree;
    unordered_map<pair<int, int>, int, PairHash> G;
    vector<vector<int>> adj;

    explicit Graph(int x)
    {
        size = x;
        maxDegree = 0;
        degree.resize(x, 0);
        adj.resize(x);
    }

    void addEdge(int u, int v)
    {
        G[{u, v}] = E.size();
        adj[u].push_back(E.size());
        adj[v].push_back(E.size());
        E.push_back({u, v});
        ++degree[u];
        ++degree[v];
        maxDegree = max({maxDegree, degree[u], degree[v]});
        return;
    }

    vector<int> edgeColoring()
    {
        vector<vector<int>> edgeColor(size, vector<int>(maxDegree + 1, -1));
        vector<int> color(E.size(), -1);
        vector<vector<int>> freeColor(size, vector<int>(maxDegree + 1));

        for (int i = 0; i < size; ++i)
        {
            iota(freeColor[i].begin(), freeColor[i].end(), 0);
        }

        auto getEdge = [&](int u, int v)
        {
            if (G.count({u, v}))
            {
                return G[{u, v}];
            }
            else
            {
                return G[{v, u}];
            }
        };

        auto findFan = [&](int u, int v)
        {
            vector<int> fan;
            vector<int> used(size, 0);
            fan.push_back(v);
            used[v] = 1;
            int c, w;
            while (1)
            {
                c = freeColor[fan.back()][0];
                if (edgeColor[u][c] == -1)
                {
                    break;
                }
                w = edgeColor[u][c];
                if (used[w])
                {
                    break;
                }
                fan.push_back(w);
                used[w] = 1;
            }
            return fan;
        };

        auto rotateFan = [&](const vector<int> &fan, int u)
        {
            if (fan.size() <= 1)
            {
                return;
            }

            for (int i = 0; i < fan.size() - 1; ++i)
            {
                popHead(freeColor[fan[i]]);
                if (color[getEdge(u, fan[i])] != -1)
                {
                    freeColor[fan[i]].push_back(color[getEdge(u, fan[i])]);
                    edgeColor[fan[i]][color[getEdge(u, fan[i])]] = -1;
                }
                color[getEdge(u, fan[i])] = color[getEdge(u, fan[i + 1])];
                edgeColor[u][color[getEdge(u, fan[i])]] = fan[i];
                edgeColor[fan[i]][color[getEdge(u, fan[i])]] = u;
            }
            int fanLast = fan[fan.size() - 1];
            freeColor[fanLast].push_back(color[getEdge(u, fanLast)]);
            edgeColor[fanLast][color[getEdge(u, fanLast)]] = -1;
            color[getEdge(u, fanLast)] = -1;
            return;
        };

        auto flipPath = [&](int u, int c, int d)
        {
            modifyValue(freeColor[u], c, d);
            while (1)
            {
                swap(edgeColor[u][c], edgeColor[u][d]);
                if (edgeColor[u][c] == -1)
                {
                    break;
                }
                color[getEdge(u, edgeColor[u][c])] = c;
                u = edgeColor[u][c];
                swap(c, d);
            }
            modifyValue(freeColor[u], d, c);
            return;
        };

        for (auto e : E)
        {
            int u = e.from, v = e.to;
            vector<int> fan = findFan(u, v);
            int d = freeColor[fan[fan.size() - 1]][0];
            if (edgeColor[u][d] == -1)
            {
                rotateFan(fan, u);
                color[getEdge(u, fan[fan.size() - 1])] = d;
                popHead(freeColor[fan[fan.size() - 1]]);
                edgeColor[u][d] = fan[fan.size() - 1];
                edgeColor[fan[fan.size() - 1]][d] = u;
                removeValue(freeColor[u], d);
            }
            else
            {
                int c = freeColor[u][0], w = 0;
                flipPath(u, c, d);
                for (int i = 0; i < fan.size(); ++i)
                {
                    if (edgeColor[fan[i]][d] == -1)
                    {
                        w = i;
                        break;
                    }
                }
                vector<int> subFan(fan.begin(), fan.begin() + w + 1);
                rotateFan(subFan, u);
                color[getEdge(u, fan[w])] = d;
                removeValue(freeColor[fan[w]], d);
                removeValue(freeColor[u], d);
                edgeColor[u][d] = fan[w];
                edgeColor[fan[w]][d] = u;
            }
        }

        return color;
    }
};

void read(int &x)
{
    x = 0;
    char ch = getchar();
    while (ch < 48 || ch > 57)
    {
        ch = getchar();
    }
    while (ch >= 48 && ch <= 57)
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return;
}

int main()
{
    int T, n, m, u, v;
    read(T);
    for (int t = 0; t < T; ++t)
    {
        read(n);
        read(m);
        Graph g(n);
        for (int j = 0; j < m; ++j)
        {
            read(u);
            read(v);
            g.addEdge(u - 1, v - 1);
        }
        vector<int> ans = g.edgeColoring();
        for (int j = 0; j < m; ++j)
        {
            printf("%d ", ans[j] + 1);
        }
        printf("\n");
    }
    return 0;
}

实际测试

由于常数过大,上述示例代码有时跑得比O(mn^2)的算法还慢。。。

评论区