欧拉路、Fleury算法、有向图的欧拉路



前言

会欧拉路之后基本上能秒杀一笔画问题 ヾ(o◕∀◕)ノヾ
参考资料蒟蒻建议是HihoCoder的欧拉路三题

欧拉路·一 - HihoCoder - 1176


思路 —— 欧拉路存在的判定
不需要真的加边,只需要判断每个点的度数,度数为奇数的点的个数为0或2即可

  #include <iostream>
  #include <cstdio>
  #include <map>
  #include <cstring>
  #include <algorithm>
  using namespace std;
  const int N = 1e4 + 5;
  const int M = 5e4 + 5;
  typedef long long ll;

  int  dg[N];

  inline void init(){
      memset(dg, 0, sizeof(dg));
  }

  bool judge(int n){
      int cnt = 0;
      for(int i = 1; i <= n; i++){
          if(dg[i]&1) cnt++;
      }
      return (cnt == 0 || cnt == 2);
  }

  int main(){
      int n, m;
      while(~scanf("%d%d", &n, &m)){
          while(m--){
              int u, v;
              scanf("%d%d", &u, &v);
              dg[v]++;
              dg[u]++;
          }
          puts(judge(n) ? "Full" : "Part");
      }
  }


欧拉路·二 - HihoCoder - 1181


思路 —— Fleury算法找欧拉路
Fleury算法模板题

  #include <iostream>
  #include <cstdio>
  #include <map>
  #include <cstring>
  #include <algorithm>
  using namespace std;
  const int N = 1e3 + 5;
  const int M = 5e3 + 5;
  typedef long long ll;

  struct edge{
      int v, nxt;
  };
  edge e[M << 1];
  int  head[N], tot, dg[N];
  int  ans[M << 1], pp;
  bool used[M << 1];

  inline void init(){
      memset(dg, 0, sizeof(dg));
      memset(head, -1, sizeof(head));
      memset(used, false, sizeof(used));
      tot = 0, pp = 0;
  }

  inline void addEdge(int u, int v){
      e[tot] = edge{v, head[u]};
      head[u] = tot++;
      dg[v]++;
  }

  int getSrc(int n){
      for(int i = 1; i <= n; i++){
          if(dg[i]&1){
              return i;
          }
      }
      return 1;
  }

  void dfs(int u){
      for(int i = head[u]; ~i; i = e[i].nxt){
          int v = e[i].v;
          if(used[i])    continue;
          used[i] = used[i^1] = true;
          dfs(v);
      }
      ans[pp++] = u;
  }

  int main(){
      int n, m;
      while(~scanf("%d%d", &n, &m)){
          init();
          while(m--){
              int u, v;
              scanf("%d%d", &u, &v);
              addEdge(u, v);
              addEdge(v, u);
          }
          dfs(getSrc(n));
          for(int i = 0; i < pp; i++){
              printf("%d", ans[i]);
              if(i < pp - 1)  putchar(' ');
          }
          puts("");
      }
  }


The Best Path - HDU - 5883


题意
给定一幅连通图,问是否存在一条路径能经过每条边正好一次,能的话求出经过的点权值异或最大值(按重数计)
思路
首先明显是欧拉路
先判断是否存在欧拉路,再判断是否为欧拉回路
若非欧拉回路,则权值最大值只能是经过的路径上的点的异或值
若是欧拉回路,则因为起点也是终点,会经过两次所以异或值不计,路径的异或值就是除起点外经过的点的异或值
最后一个问题是,如何才能知道一个点经过了几次?这与度数相关,要形成欧拉路,除了起点和终点,其他点必定入度=出度,因此 度数/2 就是经过的次数,而起点和终点如果不是同一个点,则是 (度数 + 1)/2,如果是同一个点,因为一进一出,因此也是 (度数 + 1)/2
现在要取得最大值,对于非欧拉回来直接算经过奇数次的点的异或值即可,对于欧拉回路则在原来的基础上枚举起点,最终结果再异或a[i]即可(原来经过奇数次,现在会经过偶数次,反之则相反)

  #include <iostream>
  #include <cstdio>
  #include <map>
  #include <cstring>
  #include <algorithm>
  using namespace std;
  const int N = 100000 + 5;
  typedef long long ll;

  int a[N], dg[N];

  int main(){
      int t;
      scanf("%d", &t);
      while(t--){
          memset(dg, 0, sizeof(dg));

          int n, m;
          scanf("%d%d", &n, &m);
          for(int i = 1; i <= n; i++){
              scanf("%d", &a[i]);
          }
          while(m--){
              int u, v;
              scanf("%d%d", &u, &v);
              dg[u]++;
              dg[v]++;
          }

          int cnt = 0, xorsum = 0;
          for(int i = 1; i <= n; i++){
              if(dg[i]&1) cnt++;
              if(((dg[i] + 1) >> 1)&1)  xorsum ^= a[i];
          }
          if(cnt != 0 && cnt != 2)    puts("Impossible");
          else if(cnt == 2){
              printf("%d\n", xorsum);
          }else{
              int ans = xorsum ^ a[1];
              for(int i = 2; i <= n; i++){
                  ans = max(ans, xorsum ^ a[i]);
              }
              printf("%d\n", ans);
          }
      }
  }


Catenyms - POJ - 2337


题意
给定一些字符串,问能否恰巧用每个字符串一次,组成一个当前首字母与上一个字符串尾字母相同,当前尾字母与下一个字符串首字母相同的字符串(要求字典序最小)
思路 —— 有向图的欧拉路
以首位字母为点,当前字符串为边建图,那么要求的字符串就是欧拉路形成的字符串
本题要求字典序最小,因此加边前先按字典序降序排序,然后再加边,因为邻接表实际上是逆序加入的,所以到时候遍历是正序,即字典序逐渐增加的顺序
另外题目不保证图连通,因此还要注意检查答案是否是所有的边
最后注意是有向图,因此要逆序输出

  #include <iostream>
  #include <cstdio>
  #include <map>
  #include <cstring>
  #include <algorithm>
  using namespace std;
  const int N = 1000 + 5;
  const int M = 27;
  typedef long long ll;

  struct edge{
      int v, w, next;
  };
  char tmps[M];
  string s[N];
  edge e[N];
  int  head[M];
  int  tot;
  int  in[M], out[M];
  int  ans[N << 1], pp;
  bool used[N];

  inline int mabs(int x){ return x > 0 ? x : -x; }

  inline void init(){
      pp = tot = 0;
      memset(head, -1, sizeof(head));
      memset(used, false, sizeof(used));
      memset(in, 0, sizeof(in));
      memset(out, 0, sizeof(out));
  }

  inline void addEdge(int u, int v, int idx){
      e[tot] = edge{v, idx, head[u]};
      head[u] = tot++;
      out[u]++;
      in[v]++;
  }

  int getSrc(int minsrc){
      int cnt1 = 0, cnt2 = 0;
      int src = minsrc;
      for(int i = 0; i < M; i++){
          if(mabs(in[i] - out[i]) > 1)    return -1;
          if(in[i] == out[i] + 1)  {cnt1++;}
          if(in[i] == out[i] - 1)  {cnt2++; src = i;}
      }
      if((!cnt1 && !cnt2) || (cnt1 == 1 && cnt2 == 1)){
          return src;
      }else{
          return -1;
      }
  }

  void dfs(int u){
      for(int i = head[u]; ~i; i = e[i].next){
          if(used[i])     continue;
          used[i] = true;
          dfs(e[i].v);
          ans[pp++] = e[i].w;
      }
  }

  int main(){
      int t;
      scanf("%d", &t);
      while(t--){
          init();
          int n;
          scanf("%d", &n);
          for(int i = 0; i < n; i++){
              scanf("%s", tmps);
              s[i] = string(tmps);
          }
          sort(s, s + n, greater<string>());
          int src = 100;
          for(int i = 0; i < n; i++){
              addEdge(s[i][0] - 'a', s[i][s[i].size() - 1] - 'a', i);
              src = min(src, s[i][0] - 'a');
          }

          src = getSrc(src);
          if(src == -1)       puts("***");
          else{
              dfs(src);
              if(pp < n || pp < 3)      puts("***");
              else{
                  for(int i = pp - 1; i >= 0; i--){
                      printf("%s", s[ans[i]].c_str());
                      if(i > 0)   putchar('.');
                  }
                  puts("");
              }
          }
      }
  }


Tanya and Password - CodeForces - 508D


题意
从一个字符串给出所有连续三位的子区间的字符串,求原字符串
思路 —— 状压搜索
以每个字符串前两位和后两位为点,该字符串为边建图
剩下与上题一样
另外,本题卡递归式的DFS,要用非递归的 QAQ

  #include <iostream>
  #include <cstdio>
  #include <stack>
  #include <cstring>
  #include <algorithm>
  #include <cmath>
  using namespace std;
  const int M = 2e5 + 5;
  const int N = 128*128 + 5;
  typedef long long ll;

  struct edge{
      int v, w, nxt;
  };
  edge e[M];
  int  head[N], in[N], out[N], tot, pre[M];
  bool used[M];
  int  ans[M], pp;
  char s[M];

  inline void init(){
      memset(used, false, sizeof(used));
      memset(head, -1, sizeof(head));
      memset(in, 0, sizeof(in));
      memset(out, 0, sizeof(out));
      tot = pp = 0;
  }

  inline void addEdge(int u, int v, int idx){
      e[tot] = edge{v, idx, head[u]};
      head[u] = tot++;
      in[v]++;
      out[u]++;
  }

  int getSrc(int minsrc){
      int cnt1 = 0, cnt2 = 0;
      int src = minsrc;
      for(int i = 0; i < N; i++){
          if(in[i] == out[i] + 1)       {cnt1++;}
          else if(in[i] == out[i] - 1)  {cnt2++; src = i;}
          else if(in[i] != out[i])       return -1;
      }
      if((!cnt1 && !cnt2) || (cnt1 == 1 && cnt2 == 1)){
          return src;
      }else{
          return -1;
      }
  }

  void dfs(int src){
      stack<int> stk, estk;
      stk.push(src);
      while(!stk.empty()){
          int u = stk.top();
          bool flag = true;
          for(int& i = head[u]; ~i; i = e[i].nxt){
              if(used[i])     continue;
              used[i] = true;
              flag = false;
              int v = e[i].v;

              stk.push(v);
              estk.push(i);
              break;
          }
          if(flag){
              if(!estk.empty()){
                  ans[pp++] = estk.top();
                  estk.pop();
              }
              stk.pop();
          }
      }
  }

  int main(){
      int n;
      while(~scanf("%d", &n)){
          init();
          int src;
          for(int i = 0; i < n; i++){
              scanf("%s", s);
              addEdge(s[0]*128 + s[1], s[1]*128 + s[2], i);
              src = s[0] * 128 + s[1];
          }

          src = getSrc(src);
          if(src != -1)   dfs(src);

          if(src == -1 || pp < n){
              puts("NO");
          }else{
              puts("YES");
              printf("%c%c", src/128, src%128);
              for(int i = pp - 1; i >= 0; i--){
                  putchar(e[ans[i]].v%128);
              }
              puts("");
          }
      }
  }