POJ 2513 Colored Sticks 字典树+欧拉通路判定+并查集

Colored Sticks
Time Limit: 5000MS Memory Limit: 128000K
Total Submissions: 27380 Accepted: 7241

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.

题目大意:

      现在你手中有很多两头涂了颜色的木棒,输入给出所有的木棒,以EOF结束,当两根木棒的各自一头具有相同颜色的时候,你就可以把这两根木棒接起来。问你是否可以把这些木棒都接起来。

解题思路:

      如果我们把一种颜色看成图上面的一个顶点,在一根木棒上面的两种颜色就可以表示一条无向边,那么我们只要找到一条路径,经过所有的点一次且仅一次,那么就输出Possible,否则输出Impossible。学过离散数学等知识的童鞋可以清楚的知道,就是要我们求一个无向图的欧拉通路。当该图是连通的,并且只有2个或者没有奇度顶点的时候,这样的通路才存在。好了,剩下来的就是对于字符串索引的一个问题了。250000的输入数据,但是每个不会超过10的长度,我们当然用字典树进行处理。他每次的查找次数不会超过10次。所以就在我们建立字典树的时候,每建立完成一个颜色的单词,就把它对应的度+1。在对结果进行判断的时候,我们理所当然要优先用并查集来判断图是否连通,要是不是连通图,那么我们就没有其他操作的必要了,直接输出Impossble。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 500008
int father[maxn];
int rank[maxn];
int deg[maxn];
int up,no;
bool vis[maxn];

struct Trie
{
    Trie *next[26];
    bool flag;
    int num;
    void init () 
    {
        memset ( next , NULL , sizeof ( next ) );
        num = -1;
        flag = false;   //存在标记
    }
};

int createTrie ( Trie *root , char *str )   //创建字典树过程
{
    int len = strlen ( str );
    Trie *p = root, *q;
    for ( int i = 0 ; i < len ; i ++ )
    {
        int id = str[i] - 'a';
        if ( p->next[id] == NULL )  
        {
            q = new Trie;
            q->init();
            p->next[id] = q;
            p = p->next[id];
        }
        else
            p = p->next[id];
    }
    if ( !p->flag )    //如果这个单词没有没创建过,那么我们给他一个编号,并且更新我们的颜色总数
    {
        p->flag = true;
        p->num = no;
        no++;
    }
    return p->num;  //一种颜色的单词创建完毕后,我们返回它编号
}

void init_set ( int x )
{
    father[x] = x;
    rank[x] = 0;
}

int find_set ( int x )
{
    if ( x != father[x] )
    {
        father[x] = find_set ( father[x] );
    }
    return father[x];
}

void union_set ( int x, int y ) //将x,y所代表的集合合并
{
    x = find_set ( x );
    y = find_set ( y );
    if ( x==y ) return ;
    if ( rank[x] > rank[y] ) father[y] = x;
    else
    {
        if ( rank[x] == rank[y] )
        {
            rank[y] ++;
        }
        father[x] = y;
    }
}

int main()
 {
     Trie *root = new Trie;
     root->init();
     char str[25],str1[12],str2[12];
     int num = 0,v=0;
     for ( int i = 0 ; i < maxn ; i ++ )
        init_set( i );
     memset ( deg , 0 , sizeof ( deg ) );
     while ( gets ( str ) )  
     {
         int len = strlen ( str ),i,j;
         for ( i = 0 ; str[i] != ' ' ; i ++ )
            str1[i] = str[i];
         str1[i] = '\0';
         for ( i = i + 1 , j = 0 ; i < len ; i ++ , j ++ )
            str2[j] = str[i];
         str2[j] = '\0';
         int a = createTrie ( root ,  str1 );
         int b = createTrie ( root , str2 );
         vis[a]=vis[b]=1;
         union_set (a , b );   //合并为一个集合,相当于一条无向边
         deg[a] ++;
         deg[b] ++;
     }  
     if ( no == 0 )
     {
          printf ( "Possible\n" );
          return 0;
     }
     for ( int i = 0 ; i < no ; i ++ )
     {
         if ( vis[i] && father[i] == i ) 
         {
             num ++;
         }
         if ( vis[i] && ( deg[i] % 2 == 1 ))
            v ++;
     }
     if ( num != 1 ) printf ( "Impossible\n" );
     else
     {
         if ( v == 0 || v == 2 ) printf ( "Possible\n" );
         else printf ( "Impossible\n" );
     }
     return 0;
 }


技巧总结:

      综合性很强,但是题目不难,只要求掌握基础的知识即可。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值