原题链接
暴力枚举:3^20 一定超时
可以对暴力进行优化:对于有n个点的连通块块,只需最多枚举3 * 2 ^ (n - 1),因为只有第一个点3种颜色都可以染,后续的点都受之前枚举过且与自己相连的点的约束,最多只能染两种颜色。而连通块之间的染色方案互不干扰,因此答案为各连通块染色方案的乘积。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 30;
int tem[N], cnt, n, m;
int g[N][N], c[N];
bool st[N];
void dfs(int u) { //将所有连通块中的点存到tem数组
st[u] = 1;
tem[cnt ++] = u;
for (int i = 1; i <= n; i ++) {
if (!st[i] && g[i][u]) dfs(i);
}
}
bool check(int u, int j) { //判断点u是否可以染成颜色j
for (int i = 1; i <= n; i ++) {
if (g[u][i] && c[i] == j) return false;
}
return true;
}
void dfs(int u, int &res) {
if (u == cnt) {
res ++; //枚举完所有点,方案数加1
return ;
}
for (int i = 1; i <= 3; i ++) {
if (check(tem[u], i)) {
c[tem[u]] = i;
dfs(u + 1, res);
c[tem[u]] = 0; //tem[u]点的颜色归为0,避免对tem[0] ~ tem[u - 1]点染色产生干扰
}
}
}
int main(){
cin >> n >> m;
while (m --) {
int a, b;
cin >> a >> b;
g[a][b] = g[b][a] = 1;
}
LL ans = 1;
for (int i = 1; i <= n; i ++) {
if (!st[i]) {
cnt = 0; //重置tem数组元素的个数
dfs(i); //获得连所有通块中的点
int t = 0; //记录连通块染色的方案数
dfs(0, t); //计算染色方案数
ans = ans * t; //不同连通块之间的方案数独立,答案是各连通块染色方案的乘积
}
}
cout << ans;
return 0;
}