SGU 121 解题手记
2007 解题报告 Popularity: 1%
周源的SGU表格已经叙述的非常详细了:(内容有字句上的修改)
给定一个无向图,要求给这个图上的边0、1染色,且保证每个度不小于2的点都至少能连出一条0边,也至少能连出一条1边。
可以知道,这个图上可能会出现很多连通块,而两个连通块之间是不会互相影响的,所以可以分别处理之。
对某一个连通块,可以随便选择一个点,对这个图进行深度优先搜索建树,并将这棵树分层:根节点为第0层,根节点的孩子为第1层,以此类推……
于是树上的每一条边也可能得到一个层数:根节点连出的边为第1层,根节点的孩子连向它们孩子的边为第2层,等等。即第i层节点和它们的父亲所连的边可以称为第i层。
如果在深度优先搜索树中,根节点有多个孩子:即根节点是一个“关键点”,那么问题就好办了:在根节点root连出去的多个孩子中,选出一个孩子p,让边root-p的颜色为0,其余根节点连出的树上的边的颜色是1。接着处理其他树上的边:如果这条边是p的子树上的,那么它的颜色就是其(层数+1) mod 2,否则颜色就是层数 mod 2。而叶子节点连出的回边,可以根据叶子节点现在“缺少”的颜色来确定,这样就能保证叶子节点连出去的边符合题意。而图中的其他边,可以任意染色:因为无论如何,这个染色方案都是符合题意的了。
另外一种情况,就是根节点只有一个孩子了,首先一样处理树上的边,让其颜色为其层数 mod 2。树上所有点连出的回边,颜色可为这个点的(层数+1) mod 2。这样的染色方法,可能有情况会导致构造出来的解释错误的,即根节点在原图上的度大于等于2,但所有能连向根节点的回边的颜色都是1:它们的另一个端点都在偶数层。当然,如果有一条连向根回边的另一个端点不是叶子节点,那么这个回边的染色方法是自由的:将其染为0色并不影响其他节点,却又得到了正确的答案。但是,如果所有这样的回边都是由叶子节点连出的呢?不妨随便选择一个这样的叶子节点,沿着深度优先搜索树不停的向上找,直到找到某一个祖先节点的度大于2,这个时候,将这条回溯路径上的边、以及叶子节点连向根的回边的颜色全部反色,就可以得到正确的答案了。最后可能有人会问:如果找不到度大于2的点呢?那么显然此时这个图是一个奇环:这是一定无解的。
这样,我们就得到了一个正确的算法了,其时间复杂度为O(N+E),空间复杂度为O(E)。
Submit 1: WA on 9。叶子节点连出的回边也要处理,都写成无所谓的颜色是不行的。比如下面这个图:
7
2 5 0
1 3 7 0
2 4 6 0
3 5 0
1 4 0
3 7 0
2 6 0
2-7那条边,就不能归为“无所谓”。
Submit 2: AC。感谢周天皇。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #include <cstdio> #include <list> using namespace std; struct point { int c,d,pre; list<int> ge,te; point():c(0),d(0),pre(0){}; } graph[101]; void dfs(int id) { for (list<int>::iterator i=graph[id].ge.begin();i!=graph[id].ge.end();++i) if (!graph[*i].pre) { graph[*i].pre=id; graph[id].te.push_back(*i); dfs(*i); } } bool clean_up(int id) { if (id!=graph[id].pre) { if (graph[id].te.size()<2) { graph[id].c=!graph[id].c; return clean_up(graph[id].pre); } else return true; } else return false; } bool color(int id,bool c,bool rootc,bool need_clean_up) { list<int>::iterator i=graph[id].te.begin(); if (id==graph[id].pre) { need_clean_up=(graph[id].te.size()==1); graph[*i].c=c; if (!color(*i,c,c,need_clean_up)) return false; ++i; } for (;i!=graph[id].te.end();++i) { graph[*i].c=!c; if (!color(*i,!c,rootc,need_clean_up)) return false; } if (need_clean_up && graph[id].te.size()==0 && graph[id].c!=rootc) for (list<int>::iterator i=graph[id].ge.begin();i!=graph[id].ge.end();++i) if (*i==graph[*i].pre) return clean_up(id); return true; } int main() { int n; scanf("%d",&n); for (int i=1;i<=n;i++) { int t; scanf("%d",&t); while (t) { graph[i].d++; graph[i].ge.push_back(t); scanf("%d",&t); } } for (int i=1;i<=n;i++) if (graph[i].pre==0 && graph[i].d) { graph[i].pre=i; dfs(i); if (!color(i,0,0,0)) { printf("No solution\n"); return 0; } } for (int j=1;j<=n;j++) { for (list<int>::iterator i=graph[j].ge.begin();i!=graph[j].ge.end();++i) { if (graph[*i].pre==j) printf("%d ",graph[*i].c+1); else if (graph[j].pre==*i) printf("%d ",graph[j].c+1); else if (graph[*i].te.size()==0) printf("%d ",!graph[*i].c+1); else if (graph[j].te.size()==0) printf("%d ",!graph[j].c+1); else printf("2 "); } printf("0\n"); } return 0; } |
- http://d.ream.at/sgu-121/
- Tags:DFS, SGU, 搜索, 构造, 染色
- (4)comments | leave a reply
- Trackback URI
June 9th, 2010 at 13:48 pm
我已经改好了,没事了神牛,但是那个点应该是你错的吧
Reply
June 9th, 2010 at 13:49 pm
这个实在是太久远了,我暂时没有时间管它。我觉得这应该是个bug。
Reply
June 9th, 2010 at 11:19 am
你的程序似乎有个bug
7
2 3 4 5 6 7 0
1 3 4 5 7 0
1 2 4 5 6 7 0
1 2 3 5 6 7 0
1 2 3 4 6 7 0
1 3 4 5 7 0
1 2 3 4 5 6 0
这组数据你跑出来无解,但是我的有解的
2 1 1 1 1 2 0
2 1 1 2 2 0
1 1 2 2 2 2 0
1 1 2 1 1 2 0
1 2 2 1 2 2 0
1 2 1 2 1 0
2 2 2 2 2 1 0
而去掉7这个点之后你又有解了。。。
大牛有空看下
可是我是wa得,你是ac的 TT
Reply
April 10th, 2008 at 16:40 pm
Reply