Poj 2513 Colored Sticks

本文介绍了一种基于无向图的欧拉路径判定算法,该算法利用字典树记录木棒两端的颜色,并采用并查集确保图的连通性,最终判断是否能将所有木棒连成一条直线。

题目大意:给出一些木棒,木棒数量不超过250000,每个木棒两端都涂有颜色,题目要求判断能否将所有木棒连成一条直线(颜色相同的木棒两端可以相连)。

思路:将木棒的两端看成节点,木棒看成边,则可以抽象出一个无向图,题目转换为判断无向图里是否存在一条欧拉路径。可以先用字典树为各个出现的颜色建索引,同时统计各个颜色出现的次数,即各个节点的度。根据欧拉路径成立的前提条件:奇数度的节点只能有0个或者2个,再利用并查集判断无向图的连通性,即可判断是否存在欧拉路径。

#include <iostream>
#include <stdio.h>
#include <memory.h>
using namespace std;
#define MAXN 26 //字符集大小
const int MAXSIZE = 500010;
typedef struct TrieNode {
	int nodeId; 
	struct TrieNode *next[MAXN];
}TrieNode;
TrieNode Memory[1000010];
int allocp =0;
int idCtr;
/*初始化*/
void InitTrieRoot(TrieNode **pRoot) {
	*pRoot = NULL;
	idCtr=1;
}
/*创建新结点*/
TrieNode *CreateTrieNode() {
	int i;
	TrieNode *p;
	p = &Memory[allocp++];
	p->nodeId = 0;
	for(i =0 ; i < MAXN ; i++) {
		p->next[i] = NULL;
	}
	return p;
}
//插入	  
int InsertTrie(TrieNode **pRoot , char*s) {
	int i , k;
	TrieNode *p;
	if(!(p =*pRoot)) {
		p =*pRoot = CreateTrieNode();
	}
	i =0;
	while(s[i]) {
		k = s[i++] -'a'; //确定branch
		if(!p->next[k])
			p->next[k] = CreateTrieNode();
		p = p->next[k];
	}
	if (p->nodeId==0) {
		p->nodeId=idCtr;
		idCtr++;
	}
	return p->nodeId;
}
int ctr[MAXSIZE];
int rank[MAXSIZE];//rank[x]表示x的秩
int parent[MAXSIZE];
//int n;//集合元素,从1到n
/* 查找x元素所在的集合,回溯时压缩路径*/
int FindSet(int x) {
	if (x!=parent[x])
		parent[x]=FindSet(parent[x]);
	return parent[x];
}
//Union按秩合并,即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小
//通过秩rank的大小来衡量元素的多少
void Union(int root1, int root2) {
	int x=FindSet(root1),y=FindSet(root2);
	if (x==y)
		return;
	if (rank[x]>rank[y])
		parent[y]=x;
	else {
		parent[x]=y;
		if (rank[x]==rank[y])
			++rank[y];
	}
}
void Initialization() {
	int i;
	memset(rank,0,sizeof(rank));
	for (i=1;i<MAXSIZE;i++)
		parent[i]=i;
}
int main()
{
	char start_str[12];
	char end_str[12];
	int start_id,end_id,i,j;
	TrieNode *root;

	InitTrieRoot(&root);
	memset(ctr,0,sizeof(ctr));
	Initialization();
	while (scanf("%s%s",&start_str,&end_str)!=EOF) {
		start_id=InsertTrie(&root,start_str);
		end_id=InsertTrie(&root,end_str);
	//	printf("%d %d\n",start_id,end_id);
		ctr[start_id]++;
		ctr[end_id]++;
		Union(start_id,end_id);
	}
	j=FindSet(1);
	for (i=1;i<idCtr;i++) {
		if (FindSet(i)!=j) {
			printf("Impossible\n");
			return 0;
		}
	}
	j=0;
	for (i=1;i<idCtr;i++) {
		if (ctr[i]%2!=0)
			j++;
	}
	if (j<3)
		printf("Possible\n");
	else
		printf("Impossible\n");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值