本文链接http://i.cnblogs.com/EditPosts.aspx?postid=5402042

题意:

  给你N个单词,让你把这些单词排成一个序列,使得每个单词的第一个字母和上一个字单词的最后一个字母相同(栗如:acm,malform,mouse),每个单词最多包含20个小写字母,最多1000个单词。让你输出这个序列,每两个单词之间有个'.',如果有多个解,输出字典序最小的那组解,如果无解输出"***"。关于字典序,个人感觉就是把最后的序列包括'.'在内看成一个字符串,来比较字典序。举个栗子:aa.ab.ba.aba 的字典序小于 aa.aba.ab.ba因为在索引为 5 的地方第一个序列的'.'小于第二个序列的'a',而不是仅仅看每个单词的第一个字母。

思路:

  把每个单词的的两端看成点,把单词看成一条有向边,栗如 atob 表示点 a 到点 b 有一条有向边。那么如果问题有解,则图中一定存在欧拉通路。所以需要首先判断底图是否存在欧拉通路,由单词所建立起来的图是一个有向图,判断有向图是否存在欧拉通路的条件有两个:

第一:底图必须连通,可以用并查集判断;

第二:可以存在2个点出度不等于入度,这两个点中一个出度比入度大1,为路径的起点,另外一个,入度比出度大1,为路径的终点。

如果满足上述两个条件,下来就需要找欧拉路径了,可以采用套圈法, 由于要输出字典序,所以需要对所有单词进行排序,由于涉及到排序,刚好可以用前向星来存储图。

注意:

  第一点:把单词看成边一定是有向的,判定条件不要和无向图搞混。

  第二点:提前排好序,不要在每次选择扩展路径时才选择字典序最小的,容易TLE。

  第三点:如果按照从升序,那么答案应该是反过来的,存在栈中就行了。

  第四点:题目中给的单词是随机的,所以需要找到起点,如果图中存在欧拉回路,即所有点的入度等于出度,那么找到出现的单词中首字母最小的就可以作为路径的起点了。如果不存在欧拉回路,仅存在欧拉通路,即存在确定的起点和确定的终点,那么起点不应该是字母最小的点了,而是确定的那个起点,即出度比入度大 1 的点。

代码:

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std; const int maxV = ;
const int maxE = ;
int indeg[maxV + ];//入度
int outdeg[maxV + ];//出度
int head[maxV + ];//确定起点为vi的第一条边的位置
int pre[maxV + ];
int vis[maxE + ];
int E, V;
//并查集
void initPre()
{
for(int i = ; i <= maxV; i++)pre[i] = i;
} int Find(int x)
{
return x == pre[x] ? x : pre[x] = Find(pre[x]);
} void mix(int x, int y)
{
int fx = Find(x);
int fy = Find(y);
if(fx > fy) pre[fx] = fy;
if(fx < fy) pre[fy] = fx;
}
//前向星的结构
struct EdgeNode
{
int from;
int to;
char w[];//单词
}edges[maxE + ]; int minst;
bool isEuler()//是否能形成欧拉通路
{
int flag1 = ;
int flag2 = ;
for(int i = ; i <= maxV; i++)
{
if(indeg[i] != outdeg[i])
{
if(indeg[i] == outdeg[i] + ) flag1++;
else if(indeg[i] == outdeg[i] - ) flag2++,minst = i;//如果存在出度比入度大 1 的点,即为起点
else return false;
}
}
if(flag1 == && flag2 == || flag1 == && flag2 == ) return true;
return false;
} bool isConnct()//连通性判断
{
int cnt = ;
for(int i = ; i <= maxV; i++)
if( (outdeg[i] != || indeg[i] != ) && pre[i] == i)
cnt++;
if(cnt == )return true;
return false;
} stack<int> ans;
void eulerDFS(int now)
{
for(int k = head[now]; edges[k].from == now && k <= E; k++)//优先访问由字典序比较小的单词构成的边
{
if(!vis[k])
{
vis[k] = ;
eulerDFS(edges[k].to);
ans.push(k); //回溯时,压入栈的一定是字典序较大的
}
}
} int cmp(EdgeNode A, EdgeNode B)
{
return strcmp(A.w, B.w) < ;
} int main()
{
int T;
scanf("%d", &T);
while(T--)
{
memset(&edges, , sizeof(EdgeNode));
memset(indeg, , sizeof(indeg));
memset(outdeg, , sizeof(outdeg));
memset(vis, , sizeof(vis));
initPre();
scanf("%d", &E);
minst = maxV + ;
for(int i = ; i <= E; i++)
{
scanf("%s", edges[i].w);
edges[i].from = edges[i].w[] - 'a' + ;
edges[i].to = edges[i].w[strlen(edges[i].w) - ] - 'a' + ;
outdeg[edges[i].from]++;
indeg[edges[i].to]++;
mix(edges[i].from, edges[i].to);
minst = min(minst, edges[i].from);//默认首字母较小的为起点
}
sort(edges + , edges + E + , cmp);//按照字典序升序排序
memset(head, -, sizeof(head));
head[edges[].from] = ;
for(int i = ; i<= E; i++)//构造head数组
{
if(edges[i].from != edges[i - ].from) head[edges[i].from] = i;
}
if(isEuler() && isConnct())
{
eulerDFS(minst);
int flag = ;
while(!ans.empty())
{
printf((flag++) ? ".%s":"%s", edges[ans.top()].w);
ans.pop();
}
printf("\n");
}
else
printf("***\n");
}
return ;
}

POJ 2337 Catenyms (欧拉图)的更多相关文章

  1. POJ 2337 Catenyms(有向欧拉图:输出欧拉路径)

    题目链接>>>>>> 题目大意: 给出一些字符串,问能否将这些字符串  按照 词语接龙,首尾相接  的规则 使得每个字符串出现一次 如果可以 按字典序输出这个字符串 ...

  2. POJ 2337 Catenyms

    http://poj.org/problem?id=2337 题意: 判断给出的单词能否首尾相连,输出字典序最小的欧拉路径. 思路: 因为要按字典序大小输出路径,所以先将字符串排序,这样加边的时候就会 ...

  3. POJ 2337 Catenyms(欧拉回(通)路:路径输出+最小字典序)

    题目链接:http://poj.org/problem?id=2337 题目大意:给你n个字符串,只有字符串首和尾相同才能连接起来.请你以最小字典序输出连接好的单词. 解题思路:跟POJ1386一个意 ...

  4. poj 2337 Catenyms 【欧拉路径】

    题目链接:http://poj.org/problem?id=2337 题意:给定一些单词,假设一个单词的尾字母与还有一个的首字母同样则能够连接.问能否够每一个单词用一次,将全部单词连接,能够则输出字 ...

  5. POJ 2337 Catenyms (有向图欧拉路径,求字典序最小的解)

    Catenyms Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8756   Accepted: 2306 Descript ...

  6. POJ 2337 Catenyms (欧拉回路)

    Catenyms Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8173   Accepted: 2149 Descript ...

  7. POJ 2337 Catenyms(有向图的欧拉通路)

    题意:给n个字符串(3<=n<=1000),当字符串str[i]的尾字符与str[j]的首字符一样时,可用dot连接.判断用所有字符串一次且仅一次,连接成一串.若可以,输出答案的最小字典序 ...

  8. Poj 2337 Catenyms(有向图DFS求欧拉通路)

    题意: 给定n个单词, 问是否存在一条欧拉通路(如acm,matal,lack), 如果存在, 输出字典序最小的一条. 分析: 这题可以看作http://www.cnblogs.com/Jadon97 ...

  9. Day 4 -E - Catenyms POJ - 2337

    A catenym is a pair of words separated by a period such that the last letter of the first word is th ...

随机推荐

  1. asp.net 常用几种下载方式

    protected void Button1_Click(object sender, EventArgs e) { /* 微软为Response对象提供了一个新的方法TransmitFile来解决使 ...

  2. 《数据结构与算法分析:C语言描述》复习——第八章“并查集”——并查集

    2014.06.18 14:16 简介: “并查集”,英文名为“union-find set”,从名字就能看出来它支持合并与查找功能.另外还有一个名字叫“disjoint set”,中文名叫不相交集合 ...

  3. 剑指Offer - 九度1387 - 斐波那契数列

    剑指Offer - 九度1387 - 斐波那契数列2013-11-24 03:08 题目描述: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项.斐波那契数列的定义如下: ...

  4. Python学习-前台开发-ajax操作

    概述 对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上. 1.传统的Web应用 一个简单操作需要 ...

  5. STL之算法使用简介

    accumlate : iterator 对标志的序列中的元素之和,加到一个由 init 指定的初始值上.重载的版本不再做加法,而是传进来的二元操作符被应用到元素上.  adjacent_differ ...

  6. .net的CLR

    搜索:CLR结构图 C#所具有的许多特点都是由CLR提供的,如类型安全(Type Checker).垃圾回收(Garbage Collector).异常处理(Exception Manager).向下 ...

  7. reinterpret_cast and const_cast

    reinterpret_cast reinterpret意为“重新解释” reinterpret_cast是C++中与C风格类型转换最接近的类型转换运算符.它让程序员能够将一种对象类型转换为另一种,不 ...

  8. atom-安装插件

    1. 安装git. 2. 安装node环境,其中集成了npm. 3. 启动git 键入命令: cd User/[yourname]/.atom/packages 进入packages目录. 4. 下载 ...

  9. Python中的多线程编程,线程安全与锁(二)

    在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...

  10. 【bzoj4994】[Usaco2017 Feb]Why Did the Cow Cross the Road III 树状数组

    题目描述 给定长度为2N的序列,1~N各处现过2次,i第一次出现位置记为ai,第二次记为bi,求满足ai<aj<bi<bj的对数 样例输入 4 3 2 4 4 1 3 2 1 样例输 ...