zoukankan      html  css  js  c++  java
  • POJ 2337 Catenyms (欧拉图)

    本文链接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 的点。

    代码:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <cstdlib>
      5 #include <cmath>
      6 #include <algorithm>
      7 #include <stack>
      8 #include <queue>
      9 using namespace std;
     10 
     11 const int maxV = 26;
     12 const int maxE = 1000;
     13 int indeg[maxV + 7];//入度
     14 int outdeg[maxV + 7];//出度
     15 int head[maxV + 7];//确定起点为vi的第一条边的位置
     16 int pre[maxV + 7];
     17 int vis[maxE + 7];
     18 int E, V;
     19 //并查集
     20 void initPre()
     21 {
     22     for(int i = 0; i <= maxV; i++)pre[i] = i;
     23 }
     24 
     25 int Find(int x)
     26 {
     27     return x == pre[x] ? x : pre[x] = Find(pre[x]);
     28 }
     29 
     30 void mix(int x, int y)
     31 {
     32     int fx = Find(x);    
     33     int fy = Find(y);    
     34     if(fx > fy) pre[fx] = fy;    
     35     if(fx < fy) pre[fy] = fx;
     36 }
     37 //前向星的结构
     38 struct EdgeNode
     39 {
     40     int from;
     41     int to;
     42     char w[33];//单词
     43 }edges[maxE + 7];
     44 
     45 int minst;
     46 bool isEuler()//是否能形成欧拉通路
     47 {
     48     int flag1 = 0;
     49     int flag2 = 0;
     50     for(int i = 1; i <= maxV; i++)
     51     {
     52         if(indeg[i] != outdeg[i])
     53         {
     54             if(indeg[i] == outdeg[i] + 1) flag1++; 
     55             else if(indeg[i] == outdeg[i] - 1) flag2++,minst = i;//如果存在出度比入度大 1 的点,即为起点
     56             else return false;
     57         } 
     58     }
     59     if(flag1 == 1 && flag2 == 1 || flag1 == 0 && flag2 == 0) return true;
     60     return false;
     61 }
     62 
     63 bool isConnct()//连通性判断
     64 {
     65     int cnt = 0;
     66     for(int i = 1; i <= maxV; i++)
     67         if( (outdeg[i] != 0 || indeg[i] != 0) && pre[i] == i)
     68             cnt++;
     69     if(cnt == 1)return true;
     70     return false;
     71 }
     72 
     73 stack<int> ans;
     74 void eulerDFS(int now)
     75 {
     76     for(int k = head[now]; edges[k].from == now && k <= E; k++)//优先访问由字典序比较小的单词构成的边
     77     {
     78         if(!vis[k])
     79         {
     80             vis[k] = 1;
     81             eulerDFS(edges[k].to);
     82             ans.push(k);    //回溯时,压入栈的一定是字典序较大的
     83         }    
     84     }    
     85 }
     86 
     87 int cmp(EdgeNode A, EdgeNode B)
     88 {
     89     return strcmp(A.w, B.w) < 0;
     90 }
     91 
     92 int main()
     93 {
     94     int T;
     95     scanf("%d", &T);
     96     while(T--)
     97     {
     98         memset(&edges, 0, sizeof(EdgeNode));
     99         memset(indeg, 0, sizeof(indeg));
    100         memset(outdeg, 0, sizeof(outdeg));
    101         memset(vis, 0, sizeof(vis));
    102         initPre();
    103         scanf("%d", &E);
    104         minst = maxV + 1;
    105         for(int i = 1; i <= E; i++)
    106         {
    107             scanf("%s", edges[i].w);
    108             edges[i].from = edges[i].w[0] - 'a' + 1;                  
    109             edges[i].to = edges[i].w[strlen(edges[i].w) - 1] - 'a' + 1;
    110             outdeg[edges[i].from]++;
    111             indeg[edges[i].to]++;
    112             mix(edges[i].from, edges[i].to);
    113             minst = min(minst, edges[i].from);//默认首字母较小的为起点
    114         }
    115         sort(edges + 1, edges + E + 1, cmp);//按照字典序升序排序
    116         memset(head, -1, sizeof(head));
    117         head[edges[0].from] = 0;
    118         for(int i = 1; i<= E; i++)//构造head数组
    119         {
    120             if(edges[i].from != edges[i - 1].from) head[edges[i].from] = i;
    121         } 
    122         if(isEuler() && isConnct())
    123         {
    124             eulerDFS(minst);
    125             int flag = 0;
    126             while(!ans.empty())
    127             {
    128                 printf((flag++) ? ".%s":"%s", edges[ans.top()].w);
    129                 ans.pop();
    130             }
    131             printf("
    ");
    132         }
    133         else
    134             printf("***
    ");
    135     }
    136     return 0;
    137 }
  • 相关阅读:
    VScode 修改中文字体
    missing KW_END at ')' near '<EOF>'
    SQL inner join, join, left join, right join, full outer join
    SQL字符替换函数translater, replace
    SQL COOKBOOK SQL经典实例代码 笔记第一章代码
    sqlcook sql经典实例 emp dept 创建语句
    dateutil 2.5.0 is the minimum required version python
    安装postgresql后找不到服务 postgresql service
    Postgres psql: 致命错误: 角色 "postgres" 不存在
    【西北师大-2108Java】第十六次作业成绩汇总
  • 原文地址:https://www.cnblogs.com/Ash-ly/p/5402042.html
Copyright © 2011-2022 走看看