题意:给定一个图,现在要给这个图上色,要求相邻的元素不能够涂上同样的颜色,一共只有两种颜色,黑色和白色。问给定的图最多能够涂上黑色点。
解法:从相邻的点不能够涂上相同的颜色我们可以得出该题的实质就是要求一个最大的点独立集,而求一个图的点独立集如果所给的图是一棵树的话,就可以通过拆点转化为二分图来解了,由于给定的图很可能成环,因此化作二分图的做法在这里不再适应。另一个对应的问题就是通过对原图建一个反图,然后对反图做一个最大团的求解,最大团保证了是反图中的最大完全子集,任何两个元素之间都有边相连,反应在原图中则是任意两两之间不存在边,也就是两两不相邻,这刚好契合的题意。
这是一个最简单的球最大团的版本:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <algorithm> using namespace std; int N, M, mp[105][105]; int ret, cnt, opt[105], st[105]; void dfs(int x) { if (x > N) { // 如果枚举了所有的节点 ret = cnt; memcpy(opt, st, sizeof (st)); // 用一个更大的极大团替代原有的极大团 return; } int flag = true; for (int i = 1; i < x; ++i) { // 检测新加入的点是否到团中的其他节点都存在一条边 if (st[i] && !mp[i][x]) { flag = false; break; } } if (flag) { // 如果该节点满足在这个团中 st[x] = 1, ++cnt; // 该节点被加入到完全子图中去 dfs(x + 1); st[x] = 0, --cnt; } if (cnt+N-x > ret) { // 跳过x节点进行搜索同时进行一个可行性判定 dfs(x + 1); } } int main() { int T, x, y; scanf("%d", &T); while (T--) { ret = cnt = 0; scanf("%d %d", &N, &M); memset(st, 0, sizeof (st)); for (int i = 0; i < 105; ++i) { fill(mp[i], mp[i]+105, 1); } while (M--) { scanf("%d %d", &x, &y); mp[x][y] = mp[y][x] = 0; } dfs(1); printf("%d\n", ret); for (int i = 1, j = 0; i <= N; ++i) { if (opt[i]) { printf(j == 0 ? "%d" : " %d", i); ++j; } } puts(""); } return 0; }