[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;
}
posted @ 2022-06-28 22:56  Pannta  阅读(104)  评论(0编辑  收藏  举报