热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

poj1637混合欧拉回路的判定

参考下面的解释:【混合图】混合图(既有有向边又有无向边的图)中欧拉环、欧拉路径的判定需要借助网络流!(1)欧拉环的判定:一开始当然是判断原图的基图是否连通,若不连通则一定不存在欧拉

   参考下面的解释:

【混合图】
混合图(既有有向边又有无向边的图)中欧拉环、欧拉路径的判定需要借助网络流!

(1)欧拉环的判定:
一开始当然是判断原图的基图是否连通,若不连通则一定不存在欧拉环或欧拉路径(不考虑度数为0的点)。

其实,难点在于图中的无向边,需要对所有的无向边定向(指定一个方向,使之变为有向边),使整个图变成一个有向欧拉图(或有向半欧拉图)。若存在一个定向满足此条件,则原图是欧拉图(或半欧拉图)否则不是。关键就是如何定向?

首先给原图中的每条无向边随便指定一个方向(称为初始定向),将原图改为有向图G‘,然后的任务就是改变G‘中某些边的方向(当然是无向边转化来的,原混合图中的有向边不能动)使其满足每个点的入度等于出度。
设D[i]为G‘中(点i的出度 - 点i的入度)。可以发现,在改变G‘中边的方向的过程中,任何点的D值的奇偶性都不会发生改变(设将边改为,则i入度加1出度减1,j入度减1出度加1,两者之差加2或减2,奇偶性不变)!而最终要求的是每个点的入度等于出度,即每个点的D值都为0,是偶数,故可得:若初始定向得到的G‘中任意一个点的D值是奇数,那么原图中一定不存在欧拉环!

若初始D值都是偶数,则将G‘改装成网络:设立源点S和汇点T,对于每个D[i]>0的点i,连边,容量为D[i]/2;对于每个D[j]<0的点j,连边,容量为-D[j]/2;G‘中的每条边在网络中仍保留,容量为1(表示该边最多只能被改变方向一次)。求这个网络的最大流,若S引出的所有边均满流,则原混合图是欧拉图,将网络中所有流量为1的中间边(就是不与S或T关联的边)在G‘中改变方向,形成的新图G‘‘一定是有向欧拉图;若S引出的边中有的没有满流,则原混合图不是欧拉图。

为什么能这样建图?
考虑网络中的一条增广路径S-->i-->...-->j-->T,将这条从i到j的路径在G‘中全部反向,则:i的入度加1出度减1,j的入度减1出度加1,路径中其它点的入度出度均不变。而i是和S相连的,因此初始D[i]>0,即i的出度大于入度,故这样反向之后D[i]减少2;同理,j是和T相连的,这样反向之后D[j]增加2。因此,若最大流中边满流(流量为初始D[i]/2),此时D[i]值就变成了0,也就是i的入度等于出度。因此只要使所有S引出的边全部满流,所有初始D值>0的点的D值将等于0,又因为将边变向后所有点的D值之和不变,所有初始D值小于0的点的D值也将等于0,而初始D值等于0的D点既不与S相连也不与T相连,所以它们是网络中的中间点,而中间点的流入量等于流出量,故它们的入度和出度一直不变,即D值一直为0。因此,整个图G‘成为欧拉图。

(2)欧拉路径的判定:
首先可以想到的是枚举欧拉路径的起点i和终点j,然后在图中添加边,再求图中是否有欧拉回路即可。但是,该算法的时间复杂度达到了O(M * 最大流的时间),需要优化。
前面已经说过,在将边变向的过程中任何点的D值的奇偶性都不会改变,而一个有向图有欧拉路径的充要条件是基图连通且有且只有一个点的入度比出度少1(作为欧拉路径的起点),有且只有一个点的入度比出度多1(作为终点),其余点的入度等于出度。这就说明,先把图中的无向边随便定向,然后求每个点的D值,若有且只有两个点的初始D值为奇数,其余的点初始D值都为偶数,则有可能存在欧拉路径(否则不可能存在)。进一步,检查这两个初始D值为奇数的点,设为点i和点j,若有D[i]>0且D[j]<0,则i作起点j作终点(否则若D[i]与D[j]同号则不存在欧拉路径),连边,求是否存在欧拉环即可(将求出的欧拉环中删去边即可)。这样只需求一次最大流。

就是转化成最大流,最一次最大流,看是不是满流

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
const int maxn = 200+10;
int n, m;   //n个顶点  m条边
int in[200+5], out[200+5];

struct Dinic
{
    int n;       //n个顶点
    struct edge
    {
        int from, to, cap;
    };
    vector<int> G[maxn];
    vector e;
    int level[maxn], iter[maxn];

    void init()
    {
        for(int i=0; i<=n; i++) G[i].clear();
        e.clear();
    }

    void add_edge(int u, int v, int cap)
    {
        e.push_back((edge){u, v, cap});
        e.push_back((edge){v, u, 0});
        int m = e.size();
        G[u].push_back(m-2);
        G[v].push_back(m-1);
    }

    void bfs(int s)
    {
        memset(level, -1, sizeof(level));
        queue<int> que;
        level[s] = 0;
        que.push(s);
        while(!que.empty())
        {
            int u = que.front();
            que.pop();
            for(int i=0; i)
            {
                edge &te = e[G[u][i]];
                if(te.cap>0 && level[te.to]<0)
                {
                    level[te.to] = level[u] + 1;
                    que.push(te.to);
                }
            }
        }
    }

    int dfs(int v, int t, int f)
    {
        if(v == t) return f;
        for(int &i=iter[v]; i)
        {
            edge &tpe = e[G[v][i]];
            if(tpe.cap>0 && level[v]<level[tpe.to])
            {
                int d = dfs(tpe.to, t, min(f, tpe.cap));
                if(d > 0)
                {
                    tpe.cap -= d;
                    e[G[v][i]^1].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }

    int max_flow(int s, int t)
    {
        int flow = 0;
        for(;;)
        {
            bfs(s);
            if(level[t]<0) return flow;
            memset(iter, 0, sizeof(iter));
            int f;
            while((f=dfs(s, t, 0x3fffffff)) > 0)
                flow += f;
        }
    }
} di;

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);   //0超级源点  n+1超级汇点
        di.n = n+2;
        di.init();
        memset(in, 0, sizeof(in));
        memset(out, 0, sizeof(out));
        for(int i=0; i)
        {
            int u, v, t;
            scanf("%d%d%d", &u, &v, &t);
            in[v]++; out[u]++;
            if(t == 0)  di.add_edge(u, v, 1);
        }
        bool flog = true;
        for(int i=1; i<=n&&flog; i++)
            if(abs(in[i]-out[i])%2 != 0) flog=false;
        if(!flog)
        {
            printf("impossible\n");
            continue;
        }
        for(int i=1; i<=n; i++)
        {
            if(in[i]<out[i])
                di.add_edge(0, i, (out[i]-in[i])/2);
            else if(in[i]>out[i])
                di.add_edge(i, n+1, (in[i]-out[i])/2);
        }
        di.max_flow(0, n+1);
        flog = true;
        for(int i=0; i2)
        {
            if(di.e[i].from==0 && di.e[i].cap!=0) flog=false;
        }
        if(flog) printf("possible\n");
        else printf("impossible\n");
    }
    return 0;
}

poj1637 混合欧拉回路的判定


推荐阅读
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
author-avatar
大眼刘
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有