图论-欧拉图

本文详细介绍了欧拉图的性质,包括有向图和无向图中欧拉通路和欧拉回路的判定条件。在有向图中,存在欧拉通路则入口点入度比出度小1,出口点入度比出度大1;存在欧拉回路则所有节点入度等于出度。无向图中,欧拉通路和回路要求特定节点度数为偶数。同时,文章阐述了使用DFS算法求解欧拉路径的原理,并解释了为何在DFS过程中必须后插节点。最后,给出了一道相关例题的解决方案。
摘要由CSDN通过智能技术生成

图论-欧拉图

欧拉回路与欧拉通路的判定

有向图

如果一个有向图存在欧拉通路,那么必定有两个点,一个是入口,一个是出口。入口点入度一定比出度小1,出口的入度一定比出度大1,这个入口和出口不存在的入边和出边称为虚拟边,而对于其他节点,入度=出度。

如果一个有向图存在欧拉回路,那么这两个虚拟边一定是同一条实际存在的边,因此有任意的节点,入度等于出度。

无向图

如果一个无向图存在欧拉通路,同理也有一个入口,一个出口,入口和出口的度数均为偶数。其余点的度均为奇数。

如果一个无向图存在欧拉回路,任意点的度均为偶数。

关于欧拉图判定的问题

P1636

分析

对于一个无向连通图,求用几笔能画完?每一次起笔都从度为奇数的节点开始画起,画到任意另一个度为奇数的点结束,去掉已经画完的边,这个图将可能被分成几个连通分量,但是奇数度的点的个数不会变,每画一次就会消掉一对奇数点,因此答案为奇数度点的个数除以2。对于欧拉图我们要特判一下,如果是欧拉图答案应该是1而不是0。

对于非连通图,我们应该通过dfs或者bfs用这个方法求每一个连通分量的笔画数,之后相加即可。

DFS求欧拉路

我们通过DFS求欧拉路的过程如下:

DFS(i):
	遍历与i联通的边(i,v):
		删除边(i,v)
		DFS(v)
	把i插入到答案序列中

网上很多博客给出了这个算法,但是没有讲明白为什么要后插点,前插点为什么不行的问题。

关于为什么后插点,我们必须搞清楚DFS的目的是什么。我们可以知道,在DFS过程中第一个返回的DFS的节点i一定是第一个无路可走的点,第一个无路可走的点一定是出口,因此把出口加入到答案序列中;同理,第二个返回的DFS一定是第二个无路可走点,一定是和出口点相连的第二个点。最后一个无路可走的点一定是入口,因此答案序列一定是一条欧拉路。

如果前插节点,那么可能先走到出口点,如果现在就把出口点插入序列中,答案必定是不对的。之所以前插不行,是因为前插的目的不符合我们DFS过程的目的。

例题

P1341

#include <bits/stdc++.h>

using namespace std;

#define FR freopen("in.txt","r",stdin)
#define FW freopen("out1.txt","w",stdout)

int graph[130][130];
int pre[130];
int deg[130];

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


void unite(int u,int v)
{
    pre[find(u)] = find(v);
}

vector<char> ans;

void dfs(int curr)
{
    for(int i = 64;i<=125;i++)
    {
        if(graph[curr][i])
        {
            graph[curr][i] = graph[i][curr] = 0;
            dfs(i);
        }
    }
    ans.push_back(curr);
}

int main()
{
    for(int i = 64;i<=125;i++)
        pre[i] = i;

    int n;
    cin >> n;
    while(n--)
    {
        string s;
        cin >> s;
        graph[s[0]][s[1]] = graph[s[1]][s[0]] = 1;
        unite(s[0],s[1]);
        deg[s[0]]++;
        deg[s[1]]++;
    }

    // connection
    int cnt = 0;
    for(int i = 64;i<=125;i++)
    {
        if(pre[i] == i && deg[i] != 0)
            cnt++;
    }
    if(cnt != 1)
    {
        cout <<  "No Solution";
        return 0;
    }

    int start = 0;
    int pstart = 0;
    cnt = 0;
    for(int i = 64;i<=125;i++)
    {
        if(deg[i] & 1)
        {
            if(!start) start = i;
            cnt++;
        }

        if(deg[i] != 0 && !pstart) pstart = i;
    }

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

    if(cnt == 0) start = pstart;

    dfs(start);
    copy(ans.rbegin(),ans.rend(),ostream_iterator<char>(cout,""));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值