• 图论(欧拉路径)


    理论:

    所有边都经过一次,若欧拉路径,起点终点相同,欧拉回路
    有向图欧拉路径:恰好一个out=in+1,一个in=out+1,其余in=out
    有向图欧拉回路:所有in=out
    无向图欧拉路径:两个点度数奇,其余偶
    无向图欧拉回路:全偶

    基础练习

    P7771 【模板】欧拉路径
    P2731 [USACO3.3] 骑马修栅栏 Riding the Fences
    P1341 无序字母对

    进阶

    P3520 [POI2011] SMI-Garbage
    题意:n点m条边以及边的目前状态目标状态,若干辆垃圾车跑欧拉回路,每次垃圾车经过改变路的状态
    给出需要跑多少次欧拉回路和每次欧拉回路的路径才能所有边实现目标
    思路:无向图欧拉回路拆环,
    欧拉回路边只经过一次,于是当前状态与目标状态相同的边不用管
    变成ol回路拆环问题,考虑在入栈的时候检查该元素是否在栈中,若在,表示成环。
    由于常见的求欧拉路的程序给出的结尾都不是开头点,所以在dfs调用后栈里面还剩下一个环,输出即可。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #define endl '\n'
    12. #define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
    13. #define ms(x,y) memset(x,y,sizeof x);
    14. #define YES cout<<"YES"<<'\n';
    15. #define NO cout<<"NO"<<'\n';
    16. #define fr(i,z,n) for(int i = z;i <= n; i++)
    17. #define ufr(i,n,z) for(int i = n;i >= z; i--)
    18. #define fer(i,x) for(int i=e.head[x];~i;i=e.next[i])
    19. typedef long long ll;
    20. const ll N= 1e6 + 10, inf = 1e18;
    21. const ll mod = 1e9 + 7;
    22. using namespace std;
    23. int n, m;
    24. int vist[N]; //标记点
    25. int vis[N<<1]; //标记边
    26. int deg[N],instack[N];
    27. int tot;
    28. stack<int>sta;
    29. vector<int>va[N];
    30. template<size_t size>
    31. struct Road {
    32. int to[size<<1], next[size<<1], head[size<<1], cnt = 1;
    33. ll w[size<<1];
    34. void add(int x, int y, ll ww) {
    35. to[cnt] = y;
    36. w[cnt] = ww;
    37. next[cnt] = head[x];
    38. head[x] = cnt++;
    39. }
    40. void clear(int n) {
    41. for (int i = 0; i <= n; i++) {
    42. head[i] = -1;
    43. }
    44. cnt = 0;
    45. }
    46. };
    47. Road e; // 无向图 * 2
    48. void dfs(int x) {
    49. vist[x] = 1;
    50. fer(i, x) {
    51. int v = e.to[i];
    52. if (!vis[i]) { //边未标记,每条无向边只能走一次
    53. vis[i] = vis[i ^ 1] = 1; //两条有向边组成无向边
    54. e.head[x] = e.next[i]; //下一次直接从nex[i]开始,降低时间复杂度
    55. dfs(v);
    56. if (instack[v]) {
    57. va[tot].push_back(v);
    58. while (sta.top()!=v) {
    59. va[tot].push_back(sta.top());
    60. instack[sta.top()] = 0;
    61. sta.pop();
    62. }
    63. va[tot].push_back(v);
    64. tot++;
    65. }
    66. else {
    67. sta.push(v);
    68. //cout << v << '\n';
    69. instack[v] = 1;
    70. }
    71. }
    72. }
    73. }
    74. void solve() {
    75. scanf("%d %d", &n, &m);
    76. e.clear(n);
    77. fr(i, 1, m) {
    78. int x, y, f, t;
    79. scanf("%d %d %d %d", &x, &y, &f, &t);
    80. if (f ^ t) { //相异,需要经过
    81. e.add(x, y, 0);
    82. e.add(y, x, 0);
    83. deg[x]++;
    84. deg[y]++;
    85. }
    86. }
    87. int flag = 0;
    88. fr(i, 1, n) {
    89. if (deg[i] % 2) { //无向图的欧拉回路应该为全偶
    90. flag = 1;
    91. break;
    92. }
    93. }
    94. if (flag) {
    95. printf("NIE\n");
    96. return;
    97. }
    98. fr(i, 1, n) {
    99. if (!vist[i]) { //点未标记
    100. dfs(i);
    101. if (instack[i]) { //多个环以i为起点,剩余最后一个环
    102. va[tot].push_back(i);
    103. while (!sta.empty()) {
    104. va[tot].push_back(sta.top());
    105. instack[sta.top()] = 0;
    106. sta.pop();
    107. }
    108. tot++;
    109. }
    110. }
    111. }
    112. printf("%d\n", tot);
    113. for (int i = 0; i < tot; i++) {
    114. printf("%d ", va[i].size()-1);
    115. for (auto it : va[i]) {
    116. printf("%d ", it);
    117. }
    118. printf("\n");
    119. }
    120. }
    121. signed main()
    122. {
    123. ios;
    124. int t = 1;
    125. //cin >> t;
    126. while (t--) {
    127. solve();
    128. }
    129. }

    P3443 [POI2006] LIS-The Postman
    题意:给定一个有向图,规定路线从1开始1结束,经过每条边恰好一次,同时给定一些序列
    序列在路线中需要连续出现,求满足的路线
    x,y<=n<=5e4,m<=2e5
    思路:难点在于序列的合并
    序列顺序要有边,同时序列的前驱和后继只能有一个,用E存边
     用pre,Next记录序列点的前驱后继在E的位置
    重新建图(将序列合并),跑欧拉回路,stack入栈起点在在E的位置
    最后还要确定每条边都用上

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #define endl '\n'
    12. #define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
    13. #define ms(x,y) memset(x,y,sizeof x);
    14. #define YES cout<<"YES"<<'\n';
    15. #define NO  cout<<"NO"<<'\n';
    16. #define fr(i,z,n) for(int i = z;i <= n; i++)
    17. #define ufr(i,n,z) for(int i = n;i >= z; i--)
    18. #define fer(i,x)   for(int i=e.head[x];i;i=e.next[i])
    19. typedef long long ll;
    20. const ll N = 1e6 + 10, inf = 1e18;
    21. const ll mod = 1e9 + 7;
    22. using namespace std;
    23. int in[N];
    24. template<size_t size>
    25. struct Road {
    26.     int to[size], next[size], head[size], cnt = 1;
    27.     ll w[size];
    28.     void add(int x, int y, ll ww) {
    29.         to[cnt] = y;
    30.         w[cnt] = ww;
    31.         next[cnt] = head[x];
    32.         head[x] = cnt++;
    33.     }
    34.     void clear(int n) {
    35.         for (int i = 0; i <= n; i++) {
    36.             head[i] = 0;
    37.         }
    38.         cnt = 1;
    39.     }
    40. };
    41. Roade;
    42. int n, m;
    43. map<int, int>H[N];
    44. pair<int, int>E[N];
    45. int pre[N], Next[N], b[N],vis[N];
    46. stack<int>sta;
    47. bool ans = 0;
    48. void dfs(int x) {
    49.     fer(i, x) {
    50.         int v = e.to[i];
    51.         if (!vis[i]) {
    52.             vis[i] = 1;
    53.             e.head[x] = e.next[i];
    54.             dfs(v);
    55.             sta.push(e.w[i]);           //入栈起点在E的位置
    56.         }
    57.     }
    58. }
    59. void solve() {
    60.     cin >> n >> m;
    61.     fr(i, 1, m) {
    62.         int x, y;
    63.         cin >> x >> y;
    64.         E[i] = make_pair(x, y);     //记录边
    65.         H[x][y] = i;    //记录边在E位置      
    66.         in[y]++;
    67.         in[x]--;
    68.     }
    69.     fr(i, 1, n) {
    70.         if (in[i]) {        //有向图的欧拉路径in==out
    71.             ans = 1;
    72.         }
    73.     }
    74.     int k;
    75.     cin >> k;
    76.     fr(i, 1, k) {             
    77.         int cnt,last;
    78.         cin >> cnt;
    79.         cin >> last;
    80.         for (int j = 1; j < cnt; j++) {
    81.             int y;
    82.             cin >> y;
    83.             if (H[last].find(y) == H[last].end()) {    //没有last->y的边
    84.                 ans = 1;
    85.             }
    86.             b[j] = H[last][y];
    87.             last = y;
    88.         }
    89.         for (int j = 2; j < cnt;j++) {    //处理前驱后继以及边的合并
    90.             if (!pre[b[j]]) {
    91.                 pre[b[j]] = b[j - 1];
    92.             }
    93.             else if (pre[b[j]] != b[j - 1]) {    //因为每条边只能出现一次
    94.                 ans = 1;
    95.             }
    96.             if (!Next[b[j-1]]) {
    97.                 Next[b[j - 1]] = b[j];
    98.             }
    99.             else if(Next[b[j-1]]!=b[j]) {
    100.                 ans = 1;
    101.             }
    102.         }
    103.      }
    104.     if (ans == 1) {
    105.         cout << "NIE" << '\n';
    106.         return;
    107.     }
    108.     int c = 0;
    109.     fr(i, 1, m) {                  //建图
    110.        if (!pre[i]) {
    111.            ++c;
    112.            int j;
    113.            for (j = i; Next[j]; j = Next[j]) c++;
    114.            e.add(E[i].first, E[j].second, i);        //i记录起点在E的位置
    115.        }
    116.     }
    117.     if (c < m || !e.head[1]) {     //判断联通(c==m)和能否从1开始
    118.         cout << "NIE" << '\n';
    119.         return;
    120.     }
    121.     fr(i, 1, n) {             //建图后需要再判断
    122.         if (in[i]) {
    123.             cout << "NIE" << '\n';
    124.             return;
    125.         }
    126.     }
    127.     dfs(1);           //欧拉回路
    128.     fr(i, 1, n) {                           
    129.         if (e.head[i]) {                 //确定每条边都用上
    130.             cout << "NIE" << '\n';
    131.             return;
    132.         }
    133.     }
    134.     cout << "TAK" << '\n';
    135.     cout << 1 <<'\n';
    136.     while (!sta.empty()) {
    137.         int j = sta.top();
    138.         sta.pop();
    139.         while (j) {
    140.             cout << E[j].second << '\n';
    141.             j = Next[j];
    142.         }
    143.     }
    144. }
    145. signed main()
    146. {
    147.     ios;
    148.     int t = 1;
    149.     //cin >> t;
    150.     while (t--) {
    151.         solve();
    152.     }
    153. }

  • 相关阅读:
    Spring @Profile注解使用和源码解析
    【shell】read -t -n1
    MATLAB算法实战应用案例精讲-【图像处理】SLAM技术详解(补充篇)
    什么是Docker和Docker-Compose?
    Tomcat下载安装以及配置(详细教程)
    从单体迁移至微服务,需要有足够的理由和勇气
    【Linux】进程间通信介绍及匿名管道使用
    Python图像处理:局部直方图均衡化和自动色彩均衡化
    Vue3.x Composition API - ToDoList 案例
    做过启动盘的U盘怎么复原?三种方法教你
  • 原文地址:https://blog.csdn.net/m0_75027890/article/details/134236323