例题:单词(UVa 10129h)
2016-09-27 22:46
260 查看
输入n(n<=10000)个单词,是否可以把这些单词排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同(例如:acm\malform\mouse)。每个单词最多包含1000个小写字母。输入中可以用重复单词
【分析】
思路:欧拉道路的应用。欧拉道路,即“一笔画”,从图中一结点出发走一条道路,每条边恰好经过一次。
首先要判断图是连通的。
对于无向图,最多只有两个奇点(度数为奇数)。且从一奇点出发,到另一奇点终止;若无奇点,则可从任意点出发,最终定会回到该点(欧拉回路)。
对于有向图,最多只能有两个点的入度不等于出度,且必须其中一点的出度恰好比入度大1(作为起点),另一点的入度比出度大1(作为终点)。对于有向图的连通性判断,白书上和网上题解都是说用基图(无向图)来判断连通性。自己想了下,用有向图来判断连通性肯定也是可以的,因为有向图如果不连通肯定也是不能形成欧拉道路的;但是有向图的连通性不好判断,比如一个链式的有向图,你比如从第一个起点去搜,才能知道它是连通的,你从其他点出发就不能得到连通的结论。
对于该题,可以把单词的首字母和尾字母看成顶点,一个单词,它的首尾字母存在一条有向边。这样,问题即为,每条边恰好经过一次。
因为字母作为顶点,题目里给的最多26个。 连通性的判断可以用bfs或dfs遍历即可,可以从某个入度或出度不为0(即26个字母中在该示例中出现了的字母)的顶点出发进行一次遍历,遍历结束后如果还有“出现了的字母”没被访问到,则说明不连通;或者也可以用dfs进行遍历所有的出现了的字母,然后计数,(相当于之前uva 572 求八连通块的个数)个数大于1则说明不连通。 需要注意,在一些地方,要判断26个字母每个字母是否出现。 在判断入度比出度大1、出度比入度大1后,不要忘了所有的点入度都等于出度也是可以的~
【代码】参考http://blog.csdn.net/primoblog/article/details/8471199
#include<stdio.h>
#include<string.h>
int str[27][27], visit[27], du[27][2];//str为邻接矩阵,记录两个节点是否连通,数组du用于记录字母的出度与入度
void read()
{
//读取字符串,记录开头与结尾的字母
int n;
char por, tear, temp;
scanf("%d", &n);
getchar();
while (n--)
{
scanf("%c", &por);
du[por - 'a'][1]++;
while ((temp = getchar()) != '\n')
tear = temp;
du[tear - 'a'][0]++;
str[por - 'a'][tear - 'a'] = 1;
}
}
void dfs(int s)
{
//深搜,找是否有通路
visit[s] = 1;
for (int i = 0; i < 26; i++)
{
if (str[s][i] && !visit[i])
dfs(i);
}
}
int solve()
{
int head = -1, tear = -1;
for (int i = 0, count = 0; i < 26; i++)
{//检查出度与入度,是否满足出度与入度相等,否则最多相差1
if (du[i][0] != du[i][1])
{
if (du[i][0] - du[i][1] >= 2 && du[i][0] - du[i][1] <= -2)
return 0;
else
{
count++;
if (du[i][1] - du[i][0] == 1)
head = i;
else
tear = i;
}
}
if (count > 2)return 0;//如果出现多个出度与入度相差1的节点,则肯定不能有且只有一条通路
}
if (head == -1 && tear == -1)//如果是欧拉回路的话
{
for (int i = 0; i < 26;i++)
if (du[i][0]){ dfs(i); break; }
}
else if (head == -1 || tear == -1)return 0;
else dfs(head);//否则从头开始搜索
for (int i = 0; i < 26;i++)
if (((du[i][0] + du[i][1]) && visit[i]) || (!(du[i][0] + du[i][1]) && !visit[i]));//看其是否为通路(即有度的节点必须被访问到)
else return 0;
return 1;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
memset(str, 0, sizeof(str));
memset(visit, 0, sizeof(visit));
memset(du, 0, sizeof(du));
read();
if (solve()) puts("Ordering is possible");
else
puts("The door cannot be opened.");
}
return 0;
}
【分析】
思路:欧拉道路的应用。欧拉道路,即“一笔画”,从图中一结点出发走一条道路,每条边恰好经过一次。
首先要判断图是连通的。
对于无向图,最多只有两个奇点(度数为奇数)。且从一奇点出发,到另一奇点终止;若无奇点,则可从任意点出发,最终定会回到该点(欧拉回路)。
对于有向图,最多只能有两个点的入度不等于出度,且必须其中一点的出度恰好比入度大1(作为起点),另一点的入度比出度大1(作为终点)。对于有向图的连通性判断,白书上和网上题解都是说用基图(无向图)来判断连通性。自己想了下,用有向图来判断连通性肯定也是可以的,因为有向图如果不连通肯定也是不能形成欧拉道路的;但是有向图的连通性不好判断,比如一个链式的有向图,你比如从第一个起点去搜,才能知道它是连通的,你从其他点出发就不能得到连通的结论。
对于该题,可以把单词的首字母和尾字母看成顶点,一个单词,它的首尾字母存在一条有向边。这样,问题即为,每条边恰好经过一次。
因为字母作为顶点,题目里给的最多26个。 连通性的判断可以用bfs或dfs遍历即可,可以从某个入度或出度不为0(即26个字母中在该示例中出现了的字母)的顶点出发进行一次遍历,遍历结束后如果还有“出现了的字母”没被访问到,则说明不连通;或者也可以用dfs进行遍历所有的出现了的字母,然后计数,(相当于之前uva 572 求八连通块的个数)个数大于1则说明不连通。 需要注意,在一些地方,要判断26个字母每个字母是否出现。 在判断入度比出度大1、出度比入度大1后,不要忘了所有的点入度都等于出度也是可以的~
【代码】参考http://blog.csdn.net/primoblog/article/details/8471199
#include<stdio.h>
#include<string.h>
int str[27][27], visit[27], du[27][2];//str为邻接矩阵,记录两个节点是否连通,数组du用于记录字母的出度与入度
void read()
{
//读取字符串,记录开头与结尾的字母
int n;
char por, tear, temp;
scanf("%d", &n);
getchar();
while (n--)
{
scanf("%c", &por);
du[por - 'a'][1]++;
while ((temp = getchar()) != '\n')
tear = temp;
du[tear - 'a'][0]++;
str[por - 'a'][tear - 'a'] = 1;
}
}
void dfs(int s)
{
//深搜,找是否有通路
visit[s] = 1;
for (int i = 0; i < 26; i++)
{
if (str[s][i] && !visit[i])
dfs(i);
}
}
int solve()
{
int head = -1, tear = -1;
for (int i = 0, count = 0; i < 26; i++)
{//检查出度与入度,是否满足出度与入度相等,否则最多相差1
if (du[i][0] != du[i][1])
{
if (du[i][0] - du[i][1] >= 2 && du[i][0] - du[i][1] <= -2)
return 0;
else
{
count++;
if (du[i][1] - du[i][0] == 1)
head = i;
else
tear = i;
}
}
if (count > 2)return 0;//如果出现多个出度与入度相差1的节点,则肯定不能有且只有一条通路
}
if (head == -1 && tear == -1)//如果是欧拉回路的话
{
for (int i = 0; i < 26;i++)
if (du[i][0]){ dfs(i); break; }
}
else if (head == -1 || tear == -1)return 0;
else dfs(head);//否则从头开始搜索
for (int i = 0; i < 26;i++)
if (((du[i][0] + du[i][1]) && visit[i]) || (!(du[i][0] + du[i][1]) && !visit[i]));//看其是否为通路(即有度的节点必须被访问到)
else return 0;
return 1;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
memset(str, 0, sizeof(str));
memset(visit, 0, sizeof(visit));
memset(du, 0, sizeof(du));
read();
if (solve()) puts("Ordering is possible");
else
puts("The door cannot be opened.");
}
return 0;
}
相关文章推荐
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 例题6-16 单词(Play On Words, UVa 10129)
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 紫书章六例题16 单词 UVA 10129(欧拉道路)
- 例题6-16 UVA 10129 Play On Words单词
- 单词(uva 10129)
- UVa 10129 单词 (有向欧拉路+并查集)
- uva-10129 单词
- UVa 10129单词(欧拉回路)
- UVa 10129 单词
- uva 10129 Play On Words(单词)求欧拉回路或欧拉通路
- 【Uva 10129】玩弄单词
- Play on Words 单词 UVA 10129 (欧拉+dfs)
- UVA 10129 单词
- Uva 10129 - Play on Words 单词接龙 欧拉道路应用