题目来源:http://poj.org/problem?id=2513
问题描述
Colored Sticks
Time Limit: 5000MS | Memory Limit: 128000K | |
Total Submissions: 39772 | Accepted: 10359 |
Description
You are given a bunch of wooden sticks. Each endpoint of each stick is colored with some color. Is it possible to align the sticks in a straight line such that the colors of the endpoints that touch are of the same color?
Input
Input is a sequence of lines, each line contains two words, separated by spaces, giving the colors of the endpoints of one stick. A word is a sequence of lowercase letters no longer than 10 characters. There is no more than 250000 sticks.
Output
If the sticks can be aligned in the desired way, output a single line saying Possible, otherwise output Impossible.
Sample Input
blue red
red violet
cyan blue
blue magenta
magenta cyan
Sample Output
Possible
Hint
Huge input,scanf is recommended.
Source
------------------------------------------------------------
题意
给定n个木棒,每个木棒两端涂有颜色。问能否把这些木棒排成一列,使得两两木棒连接处为同色。
------------------------------------------------------------
思路
1.把一种颜色视为一个节点,把一条木棒视为一条边,点集和边集构成图G,问题转化为G是否存在欧拉道路
2. 下面的关键问题是要解决给字符串编号的问题,也就是把不同的字符串赋予不同的数字编号。设有n个字符串,如果用map<char*,int>,复杂度为O(nlogn),本题n较大,会超时,因此采用字典树,复杂度为O(n)
3. 给节点编好号后,如果得到的图G是连通图且奇数度节点的个数为0(存在欧拉回路)或2(存在欧拉道路),则图G满足要求。节点的度可以用一个数组表示,而判断图是否连通可以用dfs、bfs或并查集,并查集时间效率最高。
附:并查集判断连通性的原理是如果两个点有边相连,就Union到一个set里,最后计算共有几个独立的set,图G就有几个连通分量。
说点题外话,并查集还可以用来判断无重边的图中是否有环,具体就是,如果图中有环,那一定存在这样一条边,该边的两个节点同在一个set里。
------------------------------------------------------------
代码
#include<cstdio>
#include<cstring>
struct Trie {
int cnt;
int index; // 字符串的唯一整数编号
struct Trie * next[26];
Trie (int cc) : cnt(cc)
{
index = 0; // 0表示空编号
memset(next, 0, sizeof(next));
}
};
const int NMAX = 250005;
Trie * root = new Trie(0); // 全局变量:字典树的根节点
int count = 0; // 全局变量:字符串s对应的唯一整数编号(非空编号从1开始)
int ufs[NMAX<<1]; // 并查集数组
int deg[NMAX<<1]; // 节点度数组
int Tier_insert(char * s) // 向字典树中插入一个字符串,返回该字符串的编号
{
int i, len = strlen(s), ch;
Trie * p = root;
for (i=0; i<len; i++)
{
p -> cnt++;
ch = s[i] - 'a';
if (!p->next[ch])
{
Trie * q = new Trie(1);
p->next[ch] = q;
}
else
{
p->next[ch]->cnt++;
}
p = p->next[ch];
}
if (p->cnt == 1)
{
count++; // 如果该字符串第一次出现,则给一个新编号
p->index = count; // 字符串的编号在最后一个字母节点上
ufs[count] = count; // 新元素初始化为一个单独的集合
}
return p->index; // 返回字符串的编号
}
int Find(int x) // 并查集函数:返回集合根节点编号
{
int y = x, i, j;
while (y != ufs[y])
{
y = ufs[y];
}
// 路径压缩
i = x, j;
while (i != y)
{
j = ufs[i];
ufs[i] = y;
i = j;
}
return y;
}
void Union(int x, int y) // 并查集函数:将x所在集合和y所在集合合并
{
int a = Find(x), b = Find(y);
if (a != b)
{
ufs[b] = a;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("2513.txt", "r", stdin);
#endif
char s1[11], s2[11];
int x1, x2, i, odd_deg_cnt = 0;
while (scanf("%s%s", s1, s2) != EOF)
{
x1 = Tier_insert(s1);
deg[x1]++;
x2 = Tier_insert(s2);
deg[x2]++;
if (x1 != x2)
{
Union(x1, x2);
}
}
for (i=1; i<=count; i++)
{
if (deg[i] % 2 == 1)
{
odd_deg_cnt++; // 计算度数为奇的点的个数
}
if (i > 1 && Find(i) != Find(i-1)) // 如果不在同一个集合,说明图不连通
{
printf("Impossible");
return 0;
}
}
if (odd_deg_cnt == 0 || odd_deg_cnt == 2) // 奇度点数为0,存在欧拉回路;奇度点数为2,存在欧拉道路
{
printf("Possible");
}
else
{
printf("Impossible");
}
return 0;
}