无序字母对 - C++

◇◆丶佛笑我妖孽 提交于 2020-01-31 20:26:29

无序字母对

题目描述
给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现。

输入格式 
       第一行输入一个正整数n。
       以下n行每行两个字母,表示这两个字母需要相邻。
输出格式 输出满足要求的字符串。
如果没有满足要求的字符串,请输出“No Solution”。
如果有多种方案,请输出前面的字母的ASCII编码尽可能小的(字典序最小)的方案

输入输出样例  
 输入 #1 
    4
    aZ
    tZ
    Xt
    aX
    
 输出 #1 
    XaZtX

这道题一开始就没什么思路,因为根本不会联想到图。但这是图的遍历里面出现的题,就觉得事情不简单。果不其然,这里涉及到一个叫欧拉路的问题,为此我特地去了解了一下欧拉路系列知识。其实理解也不难。

如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。
如果一个回路是欧拉路径,则称为欧拉回路。
具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图。

通俗点理解就是,你遍历一个图每条边只遍历一次便是欧拉路径。每条边只遍历两次便是欧拉回路。这里还涉及到一个奇点问题,度数为奇数的点。要形成欧拉回路,要么是0个奇点,要么是2个奇点。0个奇点便是一个连通图,必然是有欧拉路径,而2个奇点便是类似一条链,出点和终点度必须为1,也就是起点终点有向边数必须为1,其余点的有向边边数为2,便形成一个欧拉回路。

再回到此题,你必须先判断是否能形成欧拉回路,若不能则输出“No Solution”。判断条件有以下;
首先可以判断奇点大于0,并且奇点不等于2,则不能形成欧拉回路。
用并查集查询祖先,若祖先不为1,这不能形成欧拉回路。
其次奇点等于2,则是类似链式回路。
奇点等于0,则是连通图。

若能形成欧拉回路,则把最小字典排序顶点字符作为第一个顶点,然后进行dfs,往后遍历即可。
C++代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
char a[2];
int dis[150];
int e[150][150];
int f[150];
int ans[1500],t;
int getf(int u)
{
    if(u != f[u])
    {
      f[u] = getf(f[u]);
      return f[u];
    }else
    return u;
}
void merge(int u,int v)
{
   int t1,t2;
   t1 = getf(u);
   t2 = getf(v);
   if(t1 != t2)
   f[t2] = t1; 
}

void dfs(int u)
{
    for(int i = 65; i < 150; i ++)
    {
      if(e[u][i])
      {
         e[u][i] = 0;
     e[i][u] = 0;
     dfs(i); 
  }
    }
   ans[t++] = u; 
}
int main()
{
   cin >> n;
   //f数组初始化
   for(int i = 1; i < 150; i ++)
   f[i] = i; 
   for(int i = 1; i <= n; i ++)
   {
    cin >> a;
    dis[a[0]] ++;
    dis[a[1]] ++;
    e[a[0]][a[1]] = 1;
    e[a[1]][a[0]] = 1;
    merge(a[0],a[1]);
   }
int cnt = 0,head = 0;
   //先判断是否是个连通图 
   for(int i = 65; i < 150; i ++)
   {
    //说明有祖先 
    if(f[i] == i && dis[i])
    cnt ++;
   }
   //说明不是一个连通图,可能多个子图 
   if(cnt != 1)
   {
    printf("No Solution");
    return 0;
   }
   cnt = 0;
   for(int i = 65; i < 150; i ++)
   {
     if(dis[i] % 2)
       {
        cnt ++;
        if(head == 0)
        head = i;
       }
   }
   //只有0个奇数度或两个奇数度才可行 
   if(cnt && cnt != 2)
   {
    printf("No Solution");
    return 0;
   }
   //0个奇数度 说明是个连通图 
   if(head == 0)
   for(int i = 65; i < 150; i ++)
   {
    if(dis[i])
    {
       head = i;
       break;
    }
   }
   dfs(head);
   for(int i = t - 1; i >= 0; i --)
   cout << char(ans[i]);
   return 0;
} 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!