Bridges painting

题目大意:有个一无向图,给所有的边染色,如果一个点连接的边超过两个,那么最少要染一个白色和一个黑色,能否给整个图染色?不能输出“No solution”。

分析:引用连接 http://edward-mj.com/archives/445

首先构建dfs树,无向图dfs树具有的一大优点是该点只会向自己的祖先或子孙有非树边。

然后按深度交替染色。返祖边与自己的儿子涂同样的颜色。

如果dfs树中根结点度超过1,那么就找一条边染不同的颜色。

否则看根结点是否满足条件,如果不是,那么找一个与根结点相连的叶子,向上找到一个度大于2的点为止,将该路径上所以边反色,并且将根与该叶子的边反色。如果找不到度大于2的点,那么是奇环,无解。

ps.注意这组数据:

5
2 3 0
1 3 0
1 2 4 5 0
3 5 0
3 4 0

代码如下:

======================================================================================================================

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN = 107;

int c[MAXN][MAXN], N;///保存边的颜色, N个点
int father[MAXN], nSon[MAXN];
vector<int> e[MAXN];///相连接的点

void DFS(int u, int color)
{
    for(int i=0; i<e[u].size(); i++)
    {
        if(c[u][e[u][i]] != -1)
            continue;///这条边已经被访问过

        nSon[u]++;///子树数目加1
        c[e[u][i]][u] = c[u][e[u][i]] = color;

        if(!nSon[e[u][i]])
        {///如果不是连接的祖边
            father[e[u][i]] = u;
            DFS(e[u][i], color^1);
        }
    }
}

int main()
{
    int i, j, x;

    scanf("%d", &N);

    for(i=1; i<=N; i++)
    {
        while(scanf("%d", &x), x)
            e[i].push_back(x);
    }

    memset(c, -1, sizeof(c));

    for(i=1; i<=N; i++)
    {
        if(nSon[i] || father[i] || e[i].size()==0)
            continue;

        ///没有父节点,也没有儿子节点,说明未被访问过
        for(j=0; j<e[i].size(); j++)
        {///与根节点连接的第一个节点赋值为颜色0,后面的赋值为别的颜色
            if(j==0)
            {
                nSon[i]++, father[e[i][j]] = i;
                c[i][e[i][j]] = c[e[i][j]][i] = 0;
                DFS(e[i][j], 1);
            }
            else if(c[i][e[i][j]] == -1)
            {///这条边未被访问过
                nSon[i]++, father[e[i][j]] = i;
                c[i][e[i][j]] = c[e[i][j]][i] = 1;
                DFS(e[i][j], 0);
            }
        }

        if(nSon[i] >= 2 || e[i].size() == 1)
            continue;
        ///如果根节点的子树数目小于2,或者只有一个子节点

        for(j=1; j<e[i].size(); j++)
        {///从子节点往父节点查找一个节点有大于或等于两个子树的节点
            ///如果找到那么就把这条路径上的所有边取反,如果找不到,无解

            x = e[i][j];

            if(c[x][i] == 1)
                break;///不需要取反也可以

            while(father[x] != i && nSon[x] < 2)
                x = father[x];

            if(father[x] == i)
                continue;///未找到这样的节点

            x = e[i][j];
            c[x][i] = c[i][x] = 1;
            while(nSon[x] < 2)
            {///路径上的边取反
                c[x][father[x]] = c[father[x]][x] = c[x][father[x]] ^ 1;
                x = father[x];
            }

            break;
        }

        if(j == e[i].size())
            break;///无解
    }

    if(i <= N)
        printf("No solution
");
    else
    {
        for(i=1; i<=N; i++)
        {
            for(j=0; j<e[i].size(); j++)
                printf("%d ", c[i][e[i][j]]+1);
            printf("0
");
        }
    }

    return 0;
}
/**
5
2 3 0
1 3 0
1 2 4 5 0
3 5 0
3 4 0
**/
原文地址:https://www.cnblogs.com/liuxin13/p/4834535.html