[集合DP] 图的色数(高效枚举子集)

题目

给一个无向图G,把图中的结点染成尽量少的颜色,使得相邻结点颜色不同。

思路

1.状态定义:d(S),结点集S染色,所需要的颜色数的最小值。
2.边界:d(0)=0
3.答案:d(S)
4.状态转移方程:

d ( S ) = d ( S S ) + 1 ,   S S

(内部无边:不存在S`内的两个结点u和v使得u和v相邻)


本题需要枚举子集,所以需要一种高效率枚举子集的方法,如下:

for (int S0 = S; S0; S0 = (S0 - 1)&S)



关于时间复杂度的分析,LRJ写了一堆,但用到了后面的数学内容,看不懂,先留在这:
只要知道枚举一个集合的所有子集的所有子集的复杂度是 3 n
这里写图片描述

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int INF = maxn + 10;
const int maxn = 20 + 2;
const int maxsum = 1 << 20;
int n, m, G[maxn][maxn], d[maxsum], noedges[maxsum];

int main() {
    scanf("%d%d", &n, &m);
    int u, v;
    _for(i, 0, m) {
        scanf("%d%d", &u, &v);
        G[u][v] = G[v][u] = 1;
    }

    // 先预处理出noedges
    _rep(S, 0, (1 << n) - 1) {
        noedges[S] = 1;
        _rep(i, 1, n)
            if (S & (1 << i))
                _rep(j, 1, n)
                    if (i != j && G[i][j] && (S & (1 << j)))
                        noedges[S] = 0;
    }

    d[0] = 0;
    _for(S, 1, (1 << n)) {
        d[S] = INF;
        for (int S0 = S; S0; S0 = (S0 - 1)&S)
            if (noedges[S0]) d[S] = min(d[S], d[S - S0] + 1);  // 此处S-S0的操作,可以自己试一下,就是删去S中S0的部分
    }

    printf("%d\n", d[(1 << n) - 1]);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/icecab/article/details/80992743