nowcoder 203J Graph Coloring I(dfs)

题目链接

题目描述
修修在黑板上画了一些无向连通图,他发现他可以将这些图的结点用两种颜色染色,满足相邻点不同色。
澜澜不服气,在黑板上画了一个三个点的完全图。修修跟澜澜说,这个图我能找到一个简单奇环。
澜澜又在黑板上画了一个n个点m条边的无向连通图。很可惜这不是一道数数题,修修做不出来了。
澜澜非常得意,作为一位毒瘤出题人,有了好题当然要跟大家分享,于是他把这道题出给你做了。
输入描述:
第一行两个整数n,m (1≤ n,m≤ 3*105),接下来m行每行两个整数ai,bi表示一条边 (1≤ ai,bi≤ n)。
保证图连通,并且不存在重边和自环。
输出描述:
如果你能把图二染色,第一行输出0,第二行输出n个整数表示每个点的颜色 (0≤ xi≤ 1)。如果有多种合法方案,你可以输出任意一种。
如果你能找到一个简单奇环,第一行输出环长k,第二行输出k个整数表示环上结点编号 (1≤ yi≤ n),你需要保证yi和yi+1之间有边,y1和yn之间有边。如果有多种合法方案,你可以输出任意一种。
如果两种情况都是可行的,你只需要输出任意一种。
如果两种情况都是不可行的,请输出一行一个整数-1。
输入
3 2
1 2
1 3
输出
0
0 1 1
输入
3 3
1 2
1 3
2 3
输出
3
1 2 3
题意
给一个无向连通图,有两个选择:用黑色和白色给所有节点涂色,要求相邻节点颜色不能相同;或输出一个有奇数个节点的环。
分析
第一个问题是经典问题,直接选一个点开始涂色,然后用dfs或bfs的顺序为相邻的点依次涂色,涂完所有的点或者遇到矛盾的情况为止;对于第二个问题,奇数个节点的环,如果对这个环进行涂色,那么一定会矛盾,所以可以反过来再结合第一个问题,如果能涂完就直接输出涂色的方案,如果不能完成涂色,那么一定是在中途遇到了奇数个节点的环。因此为了更好的记录涂色的先后关系,这里应该用dfs,并在遇到矛盾情况时,回退到矛盾点被涂色的地方,就得到了一个奇数环。
总的来说,没有奇数环,则涂色一定成功,有奇数环,则输出奇数环,不会有两种都不满足的情况。
总结
原来“简单环”的意思就简单的是环,题意理解偏差了啊,最开始用bfs写了,然后在导出路径的时候发现根本行不通。
代码
#include <stdio.h>
#include<vector>
typedef std::vector<int> Vi;
#define maxn 300005
//链式存边
int fst[maxn], to[maxn << 1], nxt[maxn << 1], z = 0;
void add(int u, int v) {
    z++; to[z] = v;
    nxt[z] = fst[u]; fst[u] = z;
    z++; to[z] = u;
    nxt[z] = fst[v]; fst[v] = z;
}

int n, m;
int clr[maxn];
Vi rt;
int isodd/*是奇数环的标记*/, 
    rat/*涂色矛盾发生的地方,也就是奇数环的起点/终点*/, 
    ended/*完成了环的记录*/;
//直接涂色
void dfs(int cid) {
    int nid;
    for (int ln = fst[cid]; ln; ln = nxt[ln]) {
        nid = to[ln];
        if (clr[nid] == -1) {
            clr[nid] = !clr[cid];
            dfs(nid);
            if (ended)return;
            if (isodd) {
                rt.push_back(nid);
                if (cid == rat)//已经回退到环的起点
                    ended = 1; 
                return;
            }
        }
        else if (clr[nid] == clr[cid]) {//遇到矛盾,开始回退
            rat = nid;
            rt.push_back(nid);
            isodd = 1; ended = 0;
            return;
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i)clr[i] = -1;
    int ai, bi;
    for (int i = 0; i<m; ++i) {
        scanf("%d%d", &ai, &bi);
        add(ai, bi);
    }
    clr[1] = 1;
    dfs(1);
    if (isodd == 0) {
        printf("0\n");
        for (int i = 1; i <= n; ++i)printf("%d ", clr[i]);
    }
    else {
        int sz = rt.size();
        //环可能会重复记录起点,去掉就好了
        if (rt[sz - 1] == rt[0])sz--;
        printf("%d\n", sz);
        for (int i = 0; i<sz; ++i)printf("%d ", rt[i]);
    }
}

 

posted @ 2018-10-03 22:54  会打表的toby  阅读(262)  评论(0编辑  收藏  举报