[ACM] POJ 2513 Colored Sticks (Trie树,欧拉通路,并查集)

Colored Sticks
Time Limit: 5000MS   Memory Limit: 128000K
Total Submissions: 29736   Accepted: 7843

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

The UofA Local 2000.10.14


解题思路:

每根木棒两端都有一种颜色,如果两根木棒一端有相同的颜色,则可以连在一块,给出一系列木棒的颜色,问这些木棒可不可以连成一条直线。

思想为建图,一种颜色为一个节点,不同的木棒相同的颜色是一个节点,如果能连城一条直线的话,那么所建的图一定是连通图,且从起点出发,每条边只能经过一次(直线就是这样),这就想到了欧拉通路。

欧拉通路 (欧拉迹)—通过图中每条边一次且仅一次,并且过每一顶点的通路。

对于无向图来说:

G有欧拉通路的充分必要条件为:连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)

所以我们要解决两个问题:

1.怎样判断建的图是连通的?

2.怎样判断奇度顶点的个数?

对于第一个问题,采用并查集可以解决,只要所有的顶点都在一个集合里面,那么图一定是连通的,这又衍生出一个问题,使用并查集的时候,得需要节点的编号才可以,怎样确定节点的编号呢?可以采用trie树来确定节点的编号,cnt=1,按照颜色字符串输入的顺序,在trie树中建立一条路径,只要发现以前没有过该字符串,就让其id=cnt++,那么所输入的颜色节点的编号就变为了 1— cnt-1

对于第二个问题,用degree[]数组来保存每个顶点的度。无向图中,一个顶点的度就是有多少条边与其相连,所以在输入的时候,对于输入的两种颜色,对其编号进行degree[编号] ++就可以了。上面所说的奇度顶点(欧拉通路的两个端点),欧拉通路的两个端点有两种情况,一是颜色相同,那么在我们所建立的图上这两个端点是重合的,也就是奇度顶点的个数为0,另一种情况,颜色不同,那么奇度顶点就为2了,所以我们在这里要判断的是奇度顶点是否等于0或者等于2.

PS:做这道题真的学到好多东西。

参考:

http://blog.csdn.net/niushuai666/article/details/6549182

http://blog.csdn.net/lyy289065406/article/details/6647445

代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int maxn=250000;
int degree[maxn*2+1];//每个顶点的度
int parent[maxn*2+1];//根节点是谁
int cnt=1;//顶点的个数,一根棍子有两个顶点

struct Trie
{
    bool ok;
    int id;
    Trie *next[27];
    Trie()
    {
        ok=0;
        for(int i=0;i<=27;i++)
            next[i]=NULL;
    }
}root;

int hash(char *s)
{
    int len=strlen(s);
    Trie *p=&root;
    for(int i=0;i<len;i++)
    {
        int id=s[i]-'a';
        if(!p->next[id])
            p->next[id]=new Trie;
        p=p->next[id];
    }
    if(p->ok)
        return p->id;
    else
    {
        p->ok=1;
        p->id=cnt++;
    }
    return p->id;
}

void init()
{
    for(int i=1;i<=500000;i++)
        parent[i]=i;
}

int find(int x)
{
    return parent[x]==x?x:find(parent[x]);
}

void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)
        return ;
    parent[x]=y;
}


int main()
{
    char s1[11],s2[11];
    memset(degree,0,sizeof(degree));
    init();
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        int x=hash(s1);
        int y=hash(s2);
        degree[x]++,degree[y]++;
        unite(x,y);
    }
    cnt--;
    int self=0;//根节点为本身的个数
    int oddNum=0;//度为奇数的顶点的个数
    for(int i=1;i<=cnt;i++)
    {
        if(degree[i]%2)
            oddNum++;
        if(find(i)==i)
            self++;
        if(oddNum==3)//度数为奇数的定点个数>2
        {
            cout<<"Impossible"<<endl;
            return 0;
        }
        if(self==2)//不连通
        {
            cout<<"Impossible"<<endl;
            return 0;
        }
    }
    if(oddNum==0||oddNum==2)
        cout<<"Possible"<<endl;
    return 0;
}


你可能感兴趣的:([ACM] POJ 2513 Colored Sticks (Trie树,欧拉通路,并查集))