POJ-2513 Colored Sticks

Colored Sticks
题目链接
Time Limit: 5000MS Memory Limit: 128000K
Total Submissions: 38621 Accepted: 10108
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

题目大意
给出不超过250000根木棒,木棒两端有颜色,请按照一定顺序把木棒两两连接,使得所有木棒连成一条直线。
我们只可以把颜色相同的两个端点连起来。
单词(颜色)长度不超过10。

题解
已知有两种解法

第一种:
很明显就是判断欧拉通路或欧拉回路。判断连通性,所以肯定要用到并查集。然后再统计一趟入度出度,判断是否是欧拉图。
为了方便处理,我们肯定要排序。而木棒又有两头,我们就干脆把两头拆开,并带上这根木棍的信息,再排序,这样只需把相同的一段内的木棍信息合并就好了。

第二种:
和第一种的核心差不多,把排序改成用Trip树。

代码
排序+并查集

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=250005;
int n,m,fa[maxn],h[maxn];
struct js{
    char s[11];
    int id;//木棒信息
}a[maxn<<1];//一分为二后大小要开两倍
int get(int x){return (fa[x]==x)?x:fa[x]=get(fa[x]);}
bool cmp(js a,js b)
{
    int i=0;
    while (a.s[i]||b.s[i])
    {
        if (a.s[i]!=b.s[i])
          return a.s[i]<b.s[i];
        i++;
    }
    return 0;
}
void read(char *s)
{
    int x=0;char ch=getchar();
    while (ch!=EOF&&(ch<'a'||ch>'z')) ch=getchar();
    if (ch==EOF) {s[0]='@';return;}
    while (ch>='a'&&ch<='z') s[x]=ch,x++,ch=getchar();
    s[x]=0;
}
bool pd(js a,js b)
{
    int i=0;
    while (a.s[i]||b.s[i])
      if (a.s[i]!=b.s[i]) return 0;
      else i++;
    return 1;
}
int main()
{
    m=0;
    while (1)
    {
        m++;
        read(a[m].s);
        if(a[m].s[0]=='@') break;
        a[m].id=m+1>>1;
        m++;
        read(a[m].s);
        a[m].id=m+1>>1;
    }
    m--;n=m>>1;
    for (int i=1;i<=n;i++) fa[i]=i;
    sort(a+1,a+m+1,cmp);
    int now=1;h[1]=1;
    for (int i=1;i<m;i++)
    {
        if (pd(a[i],a[i+1]))
        {
            int fx=get(a[i].id),fy=get(a[i+1].id);
            if (fx!=fy) fa[fx]=fy;
            h[now]++;
        }else h[++now]=1;
    }
    int lst=get(1),s=0;
    for (int i=2;i<=n;i++)
      if (get(i)!=lst) {printf("Impossible");return 0;}
    for (int i=1;i<=now;i++)
      if (h[i]&1) s++;
    if (s<3) printf("Possible");
        else printf("Impossible");
    return 0;
}

Trip树+并查集

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=250005;
int n,m,sum,fa[maxn];
char s[11];
struct trip_node{
    int val,s;
    trip_node *nxt[26];
    trip_node(){val=0;s=0;for(int i=0;i<26;i++)nxt[i]=NULL;}//初始化
}*rot=new trip_node;
int get(int x){return (fa[x]==x)?x:fa[x]=get(fa[x]);}
void Insert(char *s,int len,int tot)
{
    trip_node *x=rot;
    for (int i=0;i<=len;i++)
    {
        int id=s[i]-'a';
        if (x->nxt[id]==NULL) x->nxt[id]=new trip_node;//创建新节点
        x=x->nxt[id];
        if (i==len)
        {
            if (!x->val) x->val=tot;
            int fx=get(x->val),fy=get(tot);
            if (fx!=fy) fa[fx]=fy;
            //并查集合并
            if ((++(x->s))&1) sum++;else sum--;
        }
    }
}
void read(char *s){s[0]=0;scanf("%s",s);}
int main()
{
    n=0;
    for (int i=1;i<=250000;i++) fa[i]=i;
    while (1)
    {
        read(s);
        if(s[0]==0) break;
        n++;
        Insert(s,strlen(s)-1,n);
        read(s);
        Insert(s,strlen(s)-1,n);
    }
    if (!n) {printf("Possible");return 0;}
    if (sum>2) {printf("Impossible");return 0;}
    int lst=get(1);
    for (int i=2;i<=n;i++)
      if (get(i)!=lst) {printf("Impossible");return 0;}
    printf("Possible");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值