欧拉回路 [模板题]

434 阅读4分钟

P1341传送门

对于这一题稍加分析查看题解可知是一道求欧拉回路(欧拉路径)的题目.

欧拉路径:在一个图中,由i点出发,将每个边遍历一次最终到达j点的一条路径。

欧拉回路:i=j时的欧拉路径。

判断欧拉回路,只要每个点的度数均为偶数即可。

判断欧拉路径,如果有且仅有两个点的度数为奇数,就会存在一条从这两个中的一个到达另一个的欧拉路径。

这一道题目有一个地方很具有迷惑性, 就是路径是在函数开始时顺序存储还是函数结束后回溯的时候逆序存储的问题, 乍一看是一样的, 其实并不一样, 详细的原因@ syksykCCC 已经给出了解释如下

可能很多人像我一样,认为这种在函数结束时逆序储存和在函数开始时顺序储存时一样的,于是就采用了后者,结果提交50分,下载了数据,看到了形如xxxx这样的字母对,不知道怎么处理。下面为了方便理解,我贴一下寻找欧拉路的递归代码:

50pts:

void circuit(char u)
{
    ans+=u;
    for(int i=0;i<g[u].size();i++)
    {
        char v=g[u][i];
        if(!vis[u][v])
        {
            vis[u][v]=vis[v][u]=true;
            circuit(v);
        }
    }
}
100pts:

void circuit(char u)
{
    for(int i=0;i<g[u].size();i++)
    {
        char v=g[u][i];
        if(!vis[u][v])
        {
            vis[u][v]=vis[v][u]=true;
            circuit(v);
        }
    }
    ans[n--]=u;
}
其中ansans是一个string类的用来储存答案的变量

可以发现区别就如上文所言,为什么会不同呢?

看一下这组数据

input:
5
xx
xa
aa
ab
bx
output 50pts:
aabxax
output 100pts:
aabxxa
怎么回事?

可以发现,50pts代码会贪心地挑较小结点访问,当访问到倒数第三个字母xx时,会毫不犹豫地前往aa,而不管这样做是否能遍历完,因此走到aa无路可走了,又回到xx遍历另外一个xx,从而得到一个错误的答案

而100pts代码的好处在于,它是在遍历结束后将该结点逆序存入答案中,这就保证了无路可走的节点aa一定是该序列的结尾(因为如果该结点还能继续访问,它就一定在那些能继续访问的点的前面),然后回到倒数第三个字母xx寻找下一个xx访问。此处不用担心xx是否也是一个结尾(那样就无解了),因为通过递归前的判断,这张图中一定存在欧拉路,也就是只有一个终点

这一题还有一个地方需要注意一下, 那就是这个图可能不是连通的, 故需要判断一下连通性, 判断连通性可以用并查集来完成, 当然, 这里也有一种巧办法, 那就是利用了欧拉回路的性质以及本题的要求, 最后应有ans == m + 1

下面附上完整的代码

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 10000 + 10;
int n, map_[MAXN][MAXN], ans, indgree[MAXN];
char res[MAXN]; //indgree是存放每个顶点的入度的

void solve(int i) {
    for (int j = 1; j <= 150; j++) {
        if (map_[i][j] > 0) {
            map_[i][j]--;
            map_[j][i]--;
            solve(j);
        }
    }
    res[++ans] = i;
    return;
}

void printRes() {
    for (int k = ans; k >= 1; k--) {
        cout << res[k];
    }
    cout << endl;
}

int main() {
    cin >> n;
    //对输入数据进行处理
    for (int i = 1; i <= n; i++) {
        string s;
        cin >> s;
        map_[s[0]][s[1]] = 1;
        map_[s[1]][s[0]] = 1;
        indgree[s[0]]++;
        indgree[s[1]]++;
    }

    //接下来判断是否存在欧拉路径或者欧拉回路
    int cnt = 0, flag = 0;
    for (int i = 1; i <= 150; i++) {
        if (indgree[i] & 1) {
            //出现了奇数度数的点
            cnt++;
            if (!flag) {
                flag = i;
            }
        }
    }

    if (cnt && cnt != 2) {
        cout << "No Solution";
        return 0;
    }

    if (!flag) {
        for (int i = 1; i <= 150; i++) {
            if (indgree[i]) {
                flag = i;
                break;
            }
        }
    }

    //以flag的值所对应的点开始寻找欧拉回路或者欧拉路径
    solve(flag);

    if (ans != n + 1) {
        cout << "No Solution";
        return 0;
    }

    printRes();
    return 0;
}