题目
给一个无向图G,把图中的结点染成尽量少的颜色,使得相邻结点颜色不同。
思路
1.状态定义:d(S),结点集S染色,所需要的颜色数的最小值。
2.边界:d(0)=0
3.答案:d(S)
4.状态转移方程:
(内部无边:不存在S`内的两个结点u和v使得u和v相邻)
本题需要枚举子集,所以需要一种高效率枚举子集的方法,如下:
for (int S0 = S; S0; S0 = (S0 - 1)&S)
关于时间复杂度的分析,LRJ写了一堆,但用到了后面的数学内容,看不懂,先留在这:
只要知道枚举一个集合的所有子集的所有子集的复杂度是
。
代码
#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;
}