[AtCoder] ABC_199_D RGB Coloring 2
传送门:RGB Coloring 2
思路:
直接暴力枚举的复杂度是 3^n,比较大,显然不能通过。
考虑整幅图连通,这时如果使用 DFS 搜索,就可以保证除搜索起点外其它点最多只有 2 种可选色,所以剪枝后复杂度会接近 2^n,这个复杂度就可以接受。但如果图中有多个连通分块,最坏的情况是所有点都不连边,这时用 DFS 就变成了简单的暴力枚举。
因为不同的连通分块之间的染色方案是相互不影响的,所以我们可以将图拆分为多个连通分块,每个分块就是前面说的连通图,可以在 2^n 时间内求出方案数,单独计算每个分块的方案数,最终结果即为所有分块的方案数之积。
代码实现上便是写两个 DFS 函数,一个用于拆分出连通块,另一个用于计算该分块中的染色方案数。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int> tup;
const int inf = 0x3f3f3f3f;
const int maxn = 25;
int g[maxn][maxn]; //邻接矩阵
int color[maxn]; //颜色标记,0为未标记,1、2、3分别代表三种不同颜色
int vis[maxn];
int dfn[maxn]; //dfs序
int n, m, CCans, cnt; //顶点数、边数,连通块中的方法数,dfn中已记录的点数
void getCC(int u) //也是dfs,用于获取连通分块
{
dfn[cnt++] = u;
vis[u] = 1;
for (int v = 0; v < n; v++)
{
if (!vis[v] && g[u][v])
getCC(v);
}
}
void dfs(int x)
{
if (x == cnt) {CCans++; return;}
for (int i = 1; i <= 3; i++)
{
int u = dfn[x];
for (int v = 0; v < n; v++)
if (g[u][v] && color[v] == i) goto bk;
color[u] = i;
dfs(x+1);
color[u] = 0;
bk:;
}
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF)
{
memset(g, 0, sizeof(g));
memset(color, 0, sizeof(color));
memset(vis, 0, sizeof(vis));
memset(dfn, 0, sizeof(dfn));
cnt = 0;
for (int i = 0; i < m; i++) //存图
{
int u, v;
scanf("%d%d", &u, &v);
u--, v--;
g[u][v] = g[v][u] = 1;
}
ll ans = 1;
for (int i = 0; i < n; i++)
{
if (vis[i]) continue;
CCans = 0;
int tmp = cnt;
getCC(i); //拆出一个连通分块
dfs(tmp); //在新拆出的连通分块中dfs求方法数
ans *= CCans; //累乘入最终结果
}
printf("%lld\n", ans);
}
return 0;
}