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 }
  • 相关阅读:
    第01组 Beta冲刺(5/5)
    第01组 Beta冲刺(4/5)
    第01组 Beta冲刺(3/5)
    第01组 Beta冲刺(2/5)
    第01组 Beta冲刺(1/5)
    2019 SDN上机第6次作业
    SDN课程阅读作业(2)
    2019 SDN上机第5次作业
    第01组 Alpha事后诸葛亮
    第01组 Alpha冲刺(6/6)
  • 原文地址:https://www.cnblogs.com/Ash-ly/p/5402042.html
Copyright © 2011-2022 走看看