( 图论专题 )【 欧拉图 】

( 图论专题 )【 欧拉图与哈密尔顿图 】

欧拉图性质

1. 欧拉路:欧拉路是指从图中任意一个点开始到图中任意一个点结束的路径,并且图中每条边通过的且只通过一次。

2. 欧拉回路:欧拉回路是指起点和终点相同的欧拉路。

3. 一个无向图存在欧拉路,当且仅当该图有0个或2个奇数度数的顶点,且该图是连通图。

4.一个无向图存在欧拉回路,当且仅当该图所有顶点度数都为偶数,且该图是连通图。

5.一个有向图存在欧拉回路,所有顶点的入度等于出度且该图是连通图。

6.一个有向图存在欧拉路:图连通,所有节点的出度等于入度;或者除两个节点以外的其余节点的入度和出度都相等,且这两个节点一个满足出度-入度==1,另一个满足入度--出度==1。

7.具有欧拉回路的图称为欧拉图(Euler Graph),具有欧拉通路而无欧拉回路的图称为半欧拉图

 


例题:The Best Path   HDU - 5883

题意:有n个湖泊, m条小河, 每条小河连接两个湖泊。 现在Alice想要从一个湖泊出发坐着小船经过所有的小河一次且仅一次,如果可能输出经过点的异或和(  p0→p1→p2  ==  p0 xor p1 xor p2 ), 不可能输出Impossible。

思路:这个题是个无向图判断是否是欧拉图。

首先,我们可以通过判断度的奇偶性来判断是否是 Impossible,因为欧拉路径存在两种,一种是欧拉通路,一种是欧拉回路。通路是起点、终点是不同点,那么我们需要将起点终点多算一次,并且起点和终点是固定的,必然是度为奇数的结点,那么我们也就知道了这种情况需要度为奇数的结点只有连个;回路呢,其实是起点、终点重合,变成了同一个点,所以呢,我们需要对这个起点多算一次即可了,但是这里起点不唯一,所以需要枚举起点。

那么问题来了,每个结点的异或次数是多少呢?实际上这个和度有关,因为我们可以想象的到的是,每次经过一个结点,除了起点和终点外,必然是一个入度和一个出度,想要让每条路都走一遍,需要把这些度都消费掉,那么每个结点的异或次数自然就是度数的一半了,然而这里还要一个小小的优化就是,异或不管次数再多,只有两种结果,一种是异或奇数次,一种是异或偶数次,因为偶数次的异或会抵消异或的效力,酱紫就好做很多了。这里需要注意的是,起点和终点多算一次异或也就行了。

所以呢,这里的第一个过程不考虑起点时,求得一个异或值后,接着在第二个过程加入起点和终点,如果是通路,就是固定起点和终点,比较容易解决,回路就需要遍历求最大值,而这里我们需要注意的是,插入起点后,结果可能比第一个过程的临时结果大,也可能小,所以需要格外注意取最的过程,不要以为只会变大哦~~~

 

代码:没判定图是否是连通的就给过了,可能数据全是连通图的
 

#include<bits/stdc++.h>
 
using namespace std;
 
const int maxn = 5e5+10;
int head[maxn];
int cnt,date[maxn],n,m;
int du[maxn];
 
int main()
{
    int listt;
    cin >> listt;
    while ( listt-- ) {
        memset(du,0,sizeof(du));
        memset(date,0,sizeof(date));
        cnt = 0;
        cin >> n >> m;
        for ( int i=1; i<=n; i++ ) {
            scanf("%d",&date[i]);
        }
        for ( int i=0; i<m; i++ ) {
            int u,v;
            scanf("%d %d",&u,&v);
            du[u] ++; du[v] ++;
        }
        int tot = 0,x1,x2;
        for ( int i=1; i<=n; i++ ) {
            if ( du[i]%2==1 ) {
                tot ++;
                du[i] ++; // 奇数度节点一定是起点或者终点,所有加一个进入和退出的度
            }
        }
        int ans = 0;
        for ( int i=1; i<=n; i++ ) {
            if ( du[i]/2%2==1 ) {   // 度数/2 是当前点会被便利几次, %2 是只有奇数次来会进行操作
                ans ^= date[i];
            }
        }
        if ( tot==1||tot>=3 ) { 
            printf("Impossible\n");
        }
        else if ( tot==2 ) {
            cout << ans << endl;
        }
        else {
            int maxx = ans;
            for ( int i=1; i<=n; i++ ) { // 从哪个点开始走,这个点就多算一次。 所有总值再异或一次。
                maxx = max(maxx,ans^date[i]);
            }
            cout << maxx << endl;
        }
    }
 
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值