[编程题]单词接龙 欧拉路径 一笔画问题

转载 2016年08月31日 10:47:30

问题描述

牛客网:

http://www.nowcoder.com/questionTerminal/417f534ca33f4bc7ba8ef716c980f839

拉姆刚开始学习英文单词,对单词排序很感兴趣。
如果给拉姆一组单词,他能够迅速确定是否可以将这些单词排列在一个列表中,使得该列表中任何单词的首字母与前一单词的为字母相同。
你能编写一个程序来帮助拉姆进行判断吗?

输入描述:

输入包含多组测试数据。

对于每组测试数据,第一行为一个正整数n,代表有n个单词。

然后有n个字符串,代表n个单词。

保证:

2<=n<=200,每个单词长度大于1且小于等于10,且所有单词都是由小写字母组成。

输出描述:

对于每组数据,输出”Yes”或”No”

输入例子:

3
abc
cdefg
ghijkl
4
abc
cdef
fghijk
xyz

输出例子:

Yes
No

问题分析

如果是必须按顺序判断,则比较简单,直接判断后一个单词的开头是否能接上上一个单词的结尾即可,如代码1。

如果不按顺序,则是一个“一笔画”问题(欧拉回路),需要建立图的模型求解,如可以查看牛客网的“西电学生”的java代码,贴在了代码2中。

或者参考”Robot_luo”的cpp代码(代码3)。

参考

一笔画问题

有向图的连通性及其分类

算法专题:欧拉回路

连通的无向图 G 有欧拉路径的充要条件是: G中奇顶点(连接的边数量为奇数的顶点)的数目等于0或者2。

连通的无向图 G 是欧拉环(存在欧拉回路)的充要条件是:G中每个顶点的度都是偶数。[2]。

一个连通的有向图可以表示为一条从顶点 u到 v的(不闭合的)欧拉路径的充要条件是: u的出度(从这个顶点发出的有向边的数量)比入度(指向这个顶点的有向边的数量)多1,v的出度比入度少1,而其它顶点的出度和入度都相等。

设D=(V,E)为一个有向图。若D的基图是连通图,则称D是弱连通图,简称为连通图。若vi,vj∈V ,vi→vj与vj→vi至少成立其一,则称D是单向连通图。若均有vi<->vj,则称D是强连通图。

代码1

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
    int num;
    while (cin >> num)
    {
        vector<string> v;

        for (int i = 0; i < num; i++)
        {
            string s;
            cin >> s;
            v.push_back(s);
        }
        string nowstr = v[0];
        char nowend = nowstr[nowstr.size()-1];
        int i;
        for (i = 1; i < num; i++)
        {
            nowstr = v[i];
            //cout << nowstr[0] << " " << nowend << endl;
            if (nowstr[0] != nowend)
            {
                cout << "No" << endl;
                break;
            }
            nowend = nowstr[nowstr.size()-1];
        }
        if (i == num)
            cout << "Yes" << endl;



    }
    return 0;
}

代码2

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            int n = scan.nextInt();
            String[] arr = new String[n];
            for (int i = 0; i < n; i++)
                arr[i] = scan.next();
            System.out.println(canArrangeWords(n, arr));
        }
        scan.close();
    }

    public static String canArrangeWords(int n, String[] arr) {
        // 26个英文字母看作26个点,用整数0-25来表示
        int[][] directedGraph = new int[26][26];// 邻接矩阵表示有向图
        int[] inDegree = new int[26]; // 顶点入度
        int[] outDegree = new int[26]; // 顶点出度
        boolean[] hasLetter = new boolean[26]; // 标记字母是否出现过
        boolean hasEuler = true; // 有狭义欧拉路径或欧拉回路标志
        for (int i = 0; i < n; i++) {
            String word = arr[i];
            char firstLetter = word.charAt(0);
            char lastLetter = word.charAt(word.length() - 1);
            outDegree[firstLetter - 'a']++;
            inDegree[lastLetter - 'a']++;
            directedGraph[firstLetter - 'a'][lastLetter - 'a'] = 1; // 有向图
            hasLetter[firstLetter - 'a'] = true;
            hasLetter[lastLetter - 'a'] = true;
        }
        int startNum = 0;
        int endNum = 0;
        for (int vertex = 0; vertex < 26; vertex++) {
            if (outDegree[vertex] - inDegree[vertex] == 1) // 起点
                startNum++;
            if (inDegree[vertex] - outDegree[vertex] == 1) // 终点
                endNum++;
            if (Math.abs(inDegree[vertex] - outDegree[vertex]) > 1) {
                hasEuler = false;
                break;
            }
        }
        boolean isEulerPath = (startNum == 1 && endNum == 1); // 这里指狭义上的欧拉路径,不包括欧拉回路
        boolean isEulerCircuit = (startNum == 0 && endNum == 0);// 欧拉回路
        if ((!isEulerPath) && (!isEulerCircuit)) { // 既不是欧拉路径也不是欧拉回路
            hasEuler = false;
        }
        // 判断是否弱连通
        int vertexNum = 0; // 点的数量
        for (int letter = 0; letter < 26; letter++) {
            if (hasLetter[letter]) {
                vertexNum++;
            }
        }
        int firstWordFirstLetter = arr[0].charAt(0) - 'a';// 以第一个单词的首字母作为起点
        hasEuler = hasEuler && isConnected(firstWordFirstLetter, vertexNum, directedGraph);
        if (hasEuler) {
            return "Yes";
        } else {
            return "No";
        }
    }

    // 判断有向图是否弱连通
    public static boolean isConnected(int start, int vertexNum, int[][] directedGraph) {
        int[][] undirectedGraph = new int[26][26];
        // 把有向图转换成无向图
        for (int i = 0; i < 26; i++) {
            for (int j = 0; j < 26; j++) {
                if (directedGraph[i][j] == 1) {
                    undirectedGraph[i][j] = 1;
                    undirectedGraph[j][i] = 1;
                }
            }
        }
        Queue<Integer> queue = new LinkedList<Integer>();
        boolean[] passedVertex = new boolean[26];
        queue.offer(start);
        // 从起点开始进行广度优先搜索,把路过的边都拆掉
        while (!queue.isEmpty()) {
            int currentVertex = queue.peek();
            passedVertex[currentVertex] = true;
            queue.poll();

            for (int vertex = 0; vertex < 26; vertex++) {
                if (undirectedGraph[currentVertex][vertex] == 1 && passedVertex[vertex] == false) {
                    undirectedGraph[currentVertex][vertex] = 0;
                    undirectedGraph[vertex][currentVertex] = 0;
                    queue.offer(vertex);
                }
            }
        }
        int passedVertexNum = 0;
        for (int vertex = 0; vertex < 26; vertex++) {
            if (passedVertex[vertex]) {
                passedVertexNum++;
            }
        }
        // 遍历到所有的点,证明无向图是连通的
        if (passedVertexNum == vertexNum) {
            return true;
        } else {
            return false;
        }
    }
}

代码3

这题如果是直接按顺序就很简单,判断下当前单词开头是不是等于前一个单词结尾就行了。
但是如果是可以调换顺序,就变成了一个一笔画的问题,判断是否存在欧拉通路
将单词建成一副图,取出每个单词的第一个字母和最后一个字母,连一条有向边。
然后根据欧拉通路的性质,判断下每个点的入度出度(奇数度的点不能大于2)
最后bfs判断图是否是连通图

#include<iostream>
#include<string>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
char str[300];
int g[30][30];
int In[30];
int Out[30];
int num[30];

void init()
{
    memset(g, 0, sizeof(g));
    memset(In, 0, sizeof(In));
    memset(Out, 0, sizeof(Out));
    memset(num, 0, sizeof(num));
}

bool bfs(int s,int n)
{
    queue <int> q;

    q.push(s);
    int mark[30];
    memset(mark, 0, sizeof(mark));
    while (!q.empty())
    {
        int front = q.front();
        mark[front] = 1;
        q.pop();
        for (int i = 0; i < 30; i++)
        {
            if (g[front][i] && mark[i] == 0)
            {
                g[front][i] = 0;
                q.push(i);
            }
        }
    }
    int ha = 0;
    for (int i = 0; i < 30 ;i++)
        if (mark[i]) ha++;

    if (ha==n)
        return true;
    return false;
}

int main()
{
    int n,s;

    while (cin >> n)
    {
        init();
        bool temp = true;
        for (int i = 0; i<n; i++)
        {
            cin >> str;
            int len = strlen(str);
            Out[str[0] - 'a']++;
            In[str[len - 1] - 'a']++;
            g[str[0] - 'a'][str[len - 1] - 'a'] = 1;
            g[str[len - 1] - 'a'][str[0] - 'a'] = 1;
            if (num[str[0] - 'a'] == 0) num[str[0] - 'a'] = 1;
            if (num[str[len - 1] - 'a'] == 0) num[str[len - 1] - 'a'] = 1;
                                    s = str[0] - 'a';
        }

        int sum1 = 0;
        int sum2 = 0;
        for (int i = 0; i < 30; i++)
        {
            if ((In[i] - Out[i]) >=1) sum1++;
            if ((In[i] - Out[i]) <= -1) sum2++;
            if (abs(In[i] - Out[i])>1) temp = false;
        }
        if (sum1 >= 2 || sum2 >= 2) temp = false;
        int ha = 0;
        for (int i = 0; i < 30; i++)
        {
            if (num[i] == 1) ha++;
        }
        temp = temp & bfs(s,ha);

        if (temp) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
}

小结

总的来说,如果要求有向图存在欧拉路径,首先要是弱连通图。然后,除了起点和终点以外,其他点的入度和出度相等。而且起点的出度比入度大1,终点的入度比出度大1。

相关文章推荐

Uva 10129 - Play on Words 单词接龙 欧拉道路应用

跟Uva 10054很像,不过这题的单词是不能反向的,所以是有向图,判断欧拉道路。 关于欧拉道路(from Titanium大神): 判断有向图是否有欧拉路 1.判断有向图的基图(即有向图转化为...
  • hcbbt
  • hcbbt
  • 2013-07-13 00:45
  • 2767

【JAVA程序】寻找最小生成树的欧拉路径,即一笔画问题

【程序背景】最近在做子图匹配的实验,对查询图需要进行预处理,得到最小生成树,然后进行后续的子图匹配工作,由于匹配过程是按照顺序依次遍历匹配的,当时程序就卡在寻找一条顺序相连的最小生成树的欧拉路径问题上...

[编程题]单词接龙

[编程题]单词接龙

nyoj 一笔画问题(并查集,欧拉路)

一笔画问题 原题链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=42 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你...

NYOJ 42 一笔画问题 (回路查询,搜索 or 欧拉)

一笔画问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用...

NYOJ 题目42 一笔画问题(欧拉路,图的连通性)

一笔画问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画...

nyoj 单词拼接(并查集判断连通性+欧拉路径)

这题还是比较难的。 首先建图方面,如果单纯的把单词作为点,能拼接的关系作为边,那么就是哈密顿图(每个点仅能走一次),难度比较大。 换一种思路,就是把每个单词看成一条有向边,由该单词的首字母指向尾字母。...

【DFS】CODE[VS] 1018&&NOIP2000提高组T3 单词接龙 (日常刷题???)

点击进入异世界一个深搜,莫名其妙卡了3天…. 一开始想到直接匹配字符串会不会太慢,于是就想到了用string自带的find()函数寻找子串位置,两个相比较的字符串位置相减,如果等于零(相互抵消)则说...

WIKI天梯【1018 单词接龙 】

题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行...

[NOIP2000P4]单词接龙

#include #include #include using namespace std; char start; string wod[21]; int vis[21]; int an...
  • D____
  • D____
  • 2017-04-21 15:28
  • 177
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)