HDU - 5883 The Best Path 欧拉通路/回路

一.题意:给你n个点和m条路的无向图 , 给你n个顶点的值 , 问你能否经过所有的边仅一次 , 若不能输出Impossible , 若能输出经过所有顶点异或的最大值。

二.分析:其实就是求欧拉通路/回路

1.异或性质:

(1)异或运算满足结合律和交换律 即 : (a^b)^c = a^(b^c) , a^b^c = b^a^c

(2)异或运算:自身异或自身的偶数次运算结果为0;奇数次运算结果为自身(x^x = 0 , x^0 = x)

(3)自反性: A^B^B = B^A^B = A^0 = A

2.欧拉通路/回路性质:

(1)概念:

无向图:
G是一个连通的无向图
(a).经过G的每条边一次并且仅一次的路径为欧拉通路(起点和终点不一定要一样)。
(b).如果欧拉通路是回路(起点和终点是同一个),则为欧拉回路。
(c).具有欧拉回路的无向图G称为欧拉图。

有向图:
D是一个有向图,D的基图(把D的有向边改为无向边)是连通的
(a).经过D的每条边一次并且仅一次的路径称为有向欧拉通路(起点和终点不一定一样)。
(b).如果有向欧拉通路是回路(起点和终点一样),那么称为有向欧拉回路。
(c).具有有向欧拉通路的有向图D称为有向欧拉图。

(2)欧拉通路/回路的判定:

无向图是欧拉通路的充要条件: 无向图是连通图 + 仅有两个节点为奇度节点(起终点)其他节点均为偶度节点

无向图是欧拉回路的充要条件: 无向图是连通图 + 所有节点均为偶度节点

有向图是欧拉通路的充要条件:有向图是连通图 + 仅有两个节点(一个节点出度比入度大1(起点),一个节点入度比出度大1(终点)),其他节点入度 = 出度

有向图为欧拉回路的充要条件:有向图是连通图 +所有节点入度 = 出度

(3)欧拉回路/通路经过每一个顶点次数:

无向图欧拉通路:每个节点经过次数 = (degree[i] + 1)/2;

无向图欧拉回路:起终点经过次数 = (degree[i] + 1)/2 + 1;其他节点经过次数 = (degree[i] + 1)/2;

三.思路:

(1)如果图不连通或者不是欧拉回路/通路 : 输出Impossible  (可用并查集判断图的连通性 + 记录度数判断欧拉图)

(2)如果是欧拉通路 : 则根据异或交换律 , 就是所有节点的异或值(没有最大最小),但是这里有个经过次数问题,如果经过为偶数次则异或为0,根据自反性不用累异或答案,如果为奇数则累异或该节点值,输出即可

(3)如果为欧拉回路:因为作为起点的那一个点需要多异或一次,所以需要判断哪一个节点为起点(终点)值最大

四.代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 7;
int pre[maxn],num[maxn],degree[maxn];
bool vis[maxn];
int n,m,res,ans,odd;
int finded(int a){//并查集
    int v = a;
    while(v!=pre[v])v = pre[v];
    int j =  a;
    while(j!=v){
        int temp = pre[j];
        pre[j] = v;
        j = temp;
    }
    return v;

}
void join(int a,int b){//合并
    int fx = finded(a);
    int fy = finded(b);
    if(fx!=fy){
        pre[fx] = fy;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        memset(degree,0,sizeof(degree));
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&n,&m);
        for(int i = 1;i<=n;i++){scanf("%d",&num[i]);pre[i] = i;}
        for(int i = 0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            degree[a]++;//记录度数
            degree[b]++;
            join(a,b);//合并
        }
        res = ans = odd = 0;
        for(int i = 1;i<=n;i++){
            if(degree[i]%2)odd++;//记录偶度节点个数
            if(!vis[finded(i)]){//记录根节点个数
                vis[finded(i)] = 1;
                res++;
            }
            if(res > 1)break;//根节点>1说明图不连通
        }
        if(res>1||(odd!=0&&odd!=2)){//图不连通或者不是欧拉通路/回路,输出Impossible
            printf("Impossible\n");
            continue;
        }
        for(int i = 1;i<=n;i++){//预处理一下
            int k = (degree[i]+1)/2;
            if(k&1)ans^=num[i];//经过次数为奇数次才累答案
        }
        if(!odd){//如果为欧拉回路
            ans = ans^num[1];//多异或上起点/终点
            for(int i = 2;i<=n;i++){//循环判断最大值
                ans = max(ans,ans^num[i]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/82048882