题目传输门:http://poj.org/problem?id=2513
题意:有很多跟棒子,棒子的两端被涂上了两种颜色,颜色相同的棒子端点可以相连起来,问是否可能把所有的棒子都以这样的方式串起来成一条直线。
容易想到把每根棒看做图的一条边,相同颜色的端点归为一个点(相当于连接起来了,粘到一起了)。于是接下来就是判断图中是否存在欧拉路的问题了。(无向图欧拉路存在判定条件:1.图联通(利用并查集判断) 2.度数为奇数的端点数为0个或2个)。Trick:用map对端点颜色进行标号会超时,改用字典树进行优化才能过时限。
AC代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
const int maxn = 500005;
char a[15];
char b[15];
int degree[maxn];
int cnt;
int par[maxn];
int rank[maxn];
//初始化n个元素
void init(int n)
{
for (int i = 1; i <= n; i++)
{
par[i] = i;
rank[i] = 0;
}
}
//查询树的根
int find(int x)
{
if (par[x] == x)
{
return x;
}
else
{
return par[x] = find(par[x]);
}
}
//合并x和y所属的集合
void unite(int x,int y)
{
x = find(x);
y = find(y);
if (x == y)
return;
if (rank[x] < rank[y])
{
par[x] = y;
}
else
{
par[y] = x;
if (rank[x] == rank[y])
rank[x]++;
}
}
typedef struct tree
{
tree *next[26];
int v;
}tree;
tree *root;
int creat(char *str)
{
int len = strlen(str);
tree *p = root,*q;
for (int i = 0; i < len; i++)
{
int id = str[i]-'a';
if (p->next[id] == NULL)
{
q = (tree *)malloc(sizeof(tree));
q->v = 0;
for (int j = 0; j < 26; j++)
{
q->next[j] = NULL;
}
p->next[id] = q;
}
p = p->next[id];
}
if (p->v == 0)//初始化时v值设为0,意义是未进行标号
{
p->v = cnt++;//树中该单词未标号,则进行标号
return p->v;//返回标号
}
else
return p->v;//该单词已进行过标号,返回该标号
}
void del(tree *root)
{
for (int i = 0; i < 26; i++)
{
if (root->next[i] != NULL)
del(root->next[i]);
}
free(root);
}
int main()
{
cnt = 1;
init(maxn-1);
root = (tree *)malloc(sizeof(tree));
for (int i = 0; i < 26; i++)
root->next[i] = NULL;
root->v = 0;
// int t = 0;
while (~scanf("%s %s", a, b))
{
// t++;
int t1 = creat(a);
int t2 = creat(b);
degree[t1]++;
degree[t2]++;
unite(t1,t2);
// if (t == 5) break;
}
bool ok = true;
int k = find(1);
int degree_cnt = 0;
for (int i = 1; i < cnt; i++)
{
if (find(i) != k)
{
ok = false;
break;
}
if (degree[i] & 1)
{
degree_cnt++;
}
}
if (degree_cnt != 0 && degree_cnt != 2)
{
ok = false;
}
if (ok) puts("Possible");
else puts("Impossible");
return 0;
}