有向图,无向图的欧拉回路和欧拉通路poj 2337

1. 欧拉回路,欧拉通路定义与判断

定义:

   欧拉回路:从图的某一个顶点出发,图中每条边走且仅走一次,最后回到出发点;如果这样的回路存在,则称之为欧拉回路。 
   欧拉路径:从图的某一个顶点出发,图中每条边走且仅走一次,最后到达某一个点;如果这样的路径存在,则称之为欧拉路径

判断:
     无向图欧拉回路判断:所有顶点的度数都为偶数。
     有向图欧拉回路判断:所有顶点的出度与入读相等。

     无向图欧拉路径判断: 之多有两个顶点的度数为奇数,其他顶点的度数为偶数。
     有向图欧拉路径判断: 至多有两个顶点的入度和出度绝对值差1(若有两个这样的顶点,则必须其中一个出度大于入度,另一个入度大于出度),其他顶点的入度与出度相等。
判断有向图和无向图是否存在欧拉回路和欧拉路径非常简单, 就是要注意要用并查集统计图的联通分量个数。保证联通分量的个数为1个上述算法才成立。

算法:
 
     下面给出求欧拉回路(路径)的伪代码:
Procedure Euler-circuit (start);
Begin
  For 顶点start的每个邻接点v Do
  If 边(start,v)未被标记 Then Begin
	将边(start,v)作上标记;
	将边(v,start)作上标记;                   //1
	Euler-circuit (v);
	将边加入栈;
  End;
End;

其中图可以用临界矩阵表示也可以用临界表或者边表表示。最后依次去除栈中的边或者顶点(按照具体的需求)得到欧拉回路或欧拉路径(该为代码针对无向图, 如果是有向图就去掉//1行)。如果存在入度和出度差1的顶点(无向图是顶点度数为奇数的顶点) 应该从出度比入度大1的顶点开始搜索。

接下来用Poj 2337 为例子  给出实现代码:

题目大意:输入n个单词,每个单词都形成一条从该单词首字母到尾字母的边,单词尾字母要与下一个单词首字母相同,若可以组成这样的路,即可以组成这样一条连着的单词串,输出路径(单词串),若有多条,则要按字典顺序输出,找不到路则输出***。
   
代码:
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAX_N = 1010;
const int MAX_VERT = 27;
struct Edge{
  int v, next;
  bool vis;
  char str[MAX_VERT];
};

struct Dict{
  char str[MAX_VERT];
};

Dict dict[MAX_N];
Edge edge[MAX_N * MAX_N / 2];
int adj[MAX_VERT];
char res[MAX_N][MAX_VERT];
int pre[MAX_VERT], rank[MAX_VERT];
int in[MAX_VERT], out[MAX_VERT];
bool used[MAX_VERT];
int n, edge_num;
int res_cnt;
bool cmp(const Dict& a, const Dict& b)
{
  return strcmp(a.str, b.str) >= 0;
}

void add_edge(int u, int v)
{
  edge[edge_num].vis = false;
  edge[edge_num].v = v;
  edge[edge_num].next = adj[u];
  adj[u] = edge_num++;
}

void init()
{
  for(size_t i = 0; i < MAX_VERT; ++i)
  {
    pre[i] = i;
    rank[i] = 0;
  }
}

int find(int x)
{
  return x == pre[x] ? x : pre[x] = find(pre[x]);
}

void Union(int x, int y)
{
  x = find(x);
  y = find(y);
  if(x == y)
    return;
  if(rank[x] > rank[y])
  {
    pre[y] = x;
  }
  else 
  {
    if(rank[x] == rank[y])
      rank[y]++;
    pre[x] = y;
  }
}
//judge Euler loop and Euler path in direction graph
//if the out and in degree are same. There exist a Euler loop and return any vertex as start
//if there have two vertex out and in degree not same. And one's out degree minus in degree equal 1
//and another vertex's in degree minus in degress equal 1. Else return -1 represent no Euler loop or path exist
int judge_euler()
{
  int comp_num = 0;
  int in_cnt = 0, out_cnt = 0;
  int res_idx = -1;
  for(size_t i = 0; i < MAX_VERT; ++i)
  {
    if(used[i])
    {
      if(i == find(i))//count the number of the components
        comp_num++;
      if(in[i] != out[i])
      {
        if(in[i] - out[i] == 1)
          in_cnt++;
        else if(out[i] - in[i] == 1)
        {
          out_cnt++;
          res_idx = i;
        }
        else 
          return -1;
      }
    }
  }
  if(comp_num != 1)//if there have more or less than 1 component return fail
    return -1;
  if(!((in_cnt == 1 && out_cnt == 1) || (in_cnt == 0 && out_cnt == 0)))
    return -1;
  if(res_idx == -1)
  {
    for(size_t i = 0; i < MAX_VERT; ++i)
    {
      if(out[i] > 0)
      {
        res_idx = i;
        break;
      }
    }
  }
  return res_idx;
}
//calculate Euler path 
void euler(int now, int idx)
{
  for(int i = adj[now]; i != -1; i = edge[i].next)
  {
    if(!edge[i].vis)
    {
      edge[i].vis = true;
      euler(edge[i].v, i);
    }
  }
  if(idx != -1)
    strcpy(res[res_cnt++], dict[idx].str);
}

int main()
{
  int t;
  scanf("%d", &t);
  while(t--)
  {
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(adj, -1, sizeof(adj));
    memset(used, false, sizeof(used));
    init();
    scanf("%d", &n);
    for(size_t i = 0; i < n; ++i)
    {
      char tmp[MAX_VERT];
      scanf("%s", &dict[i].str);
    }
    sort(dict, dict + n, cmp);
    edge_num = 0;
    res_cnt = 0;
    for(size_t i = 0; i < n; ++i)
    {
      int u = dict[i].str[0] - 'a';
      int v = dict[i].str[strlen(dict[i].str) - 1] - 'a';
      add_edge(u, v);
      used[u] = used[v] = true;
      in[v]++;
      out[u]++;
      int x = find(u);
      int y = find(v);
      if(x != y)
        Union(x, y);
    }
    int start = judge_euler();
    if(start != -1)
    {
      euler(start, -1);
      for(int i = res_cnt - 1; i >= 0; --i)
      {
        printf("%s", res[i]);
        if(i != 0)
          printf(".");
      }
      printf("\n");
    }
    else 
      printf("***\n");
  }
}





你可能感兴趣的:(算法,ACM,图论,欧拉路径)