POJ 2513 Colored Sticks (Trie字典树+欧拉通路+并查集) 解题报告

题意:

       有n根棍子,n<=250000,每根棍子两端各有一种颜色,如果两根棍子各有一个端点颜色相同,则可以把这两个端点连起来从而把这两个棍子连成线。现在给出每根棍子两端的颜色,问能否把所有棍子连成一条线。

思路:

       首先,可以想到以颜色为顶点,棍子为边构图,则所有棍子能连成线等同于该图中存在欧拉通路。回忆一下欧拉通路的充要条件:(1)该图连通;(2)度数为奇数的顶点数为0或者2。其中条件(1)经常被粗心忽略掉,其实条件(1)才是最根本的条件。

       接下来,问题就是对字符串的处理了。10个长度的字符串直接hash会爆内存,我采用的是字典树存储id值,新增一个颜色时,先在字典树中查找有没有,如果有,直接使用其id值,如果没有,在字典树中新增,总id值加1。这样一来,就可以转化为对于整数的处理问题了。     

       连通性用并查集判断,再记录每个顶点的度,就能判断是否存在欧拉通路。

       最后,这道题存在空数据,空数据要输出Possible,这个trick要注意下。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXA = 26;		//可能出现的字符种类数,如果只有小写字母就是26种
const int MAXL = 12;		//单词的最长长度
const int MAXN = 500010;
int degree[MAXN];
int father[MAXN];
struct Node
{
	bool is_str;		//标记该节点处是否构成单词
	int id;				//该单词的myhash值
	Node* nxt[MAXA];	//儿子分支
};
int color;		//当前的颜色数目,用与分配myhash值
void insert(Node *rt, const char *s)
{
	if (rt == NULL || *s == 0)
		return;
	int i;
	Node *p = rt;
	while (*s != 0)
	{
		if (p->nxt[*s-'a'] == NULL)
		{
			Node *tmp = new Node;
			for (i = 0; i < MAXA; ++i)
				tmp->nxt[i] = NULL;
			tmp->is_str = false;
			p->nxt[*s-'a'] = tmp;
			p = p->nxt[*s-'a'];
		}
		else
			p = p->nxt[*s-'a'];
		++s;
	}
	father[color] = color;
	p->id = color++;
	p->is_str = true;
}
int myhash(Node *rt, const char *s)
{
	Node *p = rt;
	char t[MAXL];
	strcpy(t,s);
	while (p != NULL && *s != 0)
	{
		p = p->nxt[*s-'a'];
		++s;
	}
	if (p != NULL && p->is_str == true)
		return p->id;
	else
	{
		insert(rt,t);
		return color-1;
	}	
}
int find(int x)
{
	if (father[x] != x)
		return x = find(father[x]);
	else
		return x;
}
void union_set(int x,int y)
{
	int px = find(x);
	int py = find(y);
	if (px != py)
		father[py] = px;
}
bool in_one_set(int x,int y)
{
	return (find(x) == find(y));
}

void del(Node *rt)
{
	int i;
	for (i = 0; i < MAXA; ++i)
	{
		if (rt->nxt[i] != NULL)
			del(rt->nxt[i]);
	}
	free(rt);
}
int main()
{
	int i;
	char s[MAXL],t[MAXL];

	//初始化字典树
	Node *rt = new Node;
	for (i = 0; i < MAXA; ++i)
		rt->nxt[i] = NULL;
	rt->is_str = false;
	memset(degree,0,sizeof(degree));
	color = 0;

	while (scanf("%s %s",s,t) != EOF)
	{
		int a = myhash(rt,s);
		int b = myhash(rt,t);
		++degree[a];
		++degree[b];
		union_set(a,b);
	}
	int res = 0;
	for (i = 0; i < color; ++i)
	{
		if (degree[i] % 2 == 1)
			++res;
	}
	if (color == 0)
		printf("Possible\n");
	else if (res == 0 || res == 2)
	{
		bool flag = false;
		for (i = 0; i < color-1; ++i)
		{
			if (in_one_set(i,i+1) == false)
				flag = true;
		}
		if (flag)
			printf("Impossible\n");
		else
			printf("Possible\n");
	}
	else
		printf("Impossible\n");
	del(rt);

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值