DAG最小路径点覆盖,最小路径可重复覆盖,详解

news2025/1/16 6:46:30

文章目录

    • 前言
    • 有向无环图的最小路径点覆盖
      • 概念
      • 拆点二分图
      • 定理
        • **证明**
      • 最小路径可重复覆盖
        • 解决策略
        • 代码实现
    • OJ练习

前言

关于二分图:二分图及染色法判定

关于二分图最大匹配:二分图最大匹配——匈牙利算法详解

关于二分图带权最大完备匹配:二分图带权最大匹配-KM算法详解


有向无环图的最小路径点覆盖

概念

给定一张有向无环图,要求用尽量少的不相交的简单路径,覆盖有向无环图的所有顶点(也就是每个顶点恰好被覆盖一次)。这个问题被称为有向无环图的最小路径点覆盖,简称**“最小路径覆盖”**。

拆点二分图

设原来的有向无环图为G = (V , E), n = |V|。 把G中的每个点x拆成编号为x和x+n的两个点。建立一张新的二分图,1~n作为二分图左部点,n + 1 ~ 2n作为二分图右部点,对于原图的每条有向边(x,y), 在二分图的左部点x与右部点y+n之间连边。最终得到的二分图称为G的拆点二分图,记为G’。例如:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

定理

有向无环图G的最小路径点覆盖包含的路径条数,等于n减去拆点二分图G’的最大匹配数。

证明

在有向无环图G= (V,E)的最小路径覆盖中,对于任意的x ∈ V,因为路径不相交,所以x的入度和出度都不超过1。因为每个节点都被覆盖,所以x的入度和出度至少有一个是1
因此,最小路径覆盖中的所有边,在拆点二分图G‘中构成一组匹配。最小路径覆盖中每条边(x,y) 的起点x与拆点二分图每条匹配边(x,y+n)的左部点x是一一对应的
特别地,对于每条路径的终点t,因为t没有出边,所以在二分图中,t匹配失败。即路径的终点和拆点二分图左部的非匹配点是一一对应的。

故有:路径覆盖包含的路径条数最少

等价于 路径的终点数(出度为0的点数)最少

等价于 拆点二分图左部非匹配点最少

等价于 拆点二分图匹配数最大

G的最小路径覆盖的路径数等于n减去拆点二分图G‘的最大匹配数
证毕。

最小路径可重复覆盖

给定一张有向无环图,要求用尽量少的可相交的简单路径,覆盖有向无环图的所有顶点(也就是一个节点可以被覆盖多次)。这个问题被称为有向无环图的最小路径可重复点覆盖

解决策略

在最小路径可重复点覆盖中,若两条路径:…→u→p→v→…和…→x→p→y→…在点p相交,则我们在原图中添加一条边(x,y),让第二条路径直接走x→y,就可以避免重复覆盖点p。

进一步地,如果我们把原图中所有间接连通的点对x,y直接连上有向边(x,y),那么任何“有路径相交的点覆盖”一定都能转化成“没有路径相交的点覆盖”。实际上,转化后的拆点二分图的未匹配左部点仍为最小可相交路径数目,例如:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

综上所述,有向无环图G的最小路径可重复点覆盖等价于先对有向图传递闭包得到有向无环图G’,再在G’上求一般的(路径不可相交的)最小路径点覆盖

代码实现

#define int long long
#define N 205

int n, m, match[N]{0}, ans = 0;
bool vis[N], g[N][N]{0};//邻接矩阵存图
//匈牙利
bool dfs(int u)
{
    for (int v = 1; v <= n; v++)
        if (!vis[v] && g[u][v])
        {
            vis[v] = true;
            if (!match[v] || dfs(match[v]))
            {
                match[v] = u;
                return true;
            }
        }
    return false;
}

//main
    // Floyd求传递闭包
    for (int i = 1; i <= n; i++)
        g[i][i] = true;

    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] |= g[i][k] && g[k][j];

    for (int i = 1; i <= n; i++)
        g[i][i] = false;

    for (int i = 1; i <= n; i++)
        memset(vis, 0, sizeof(vis)), ans += dfs(i);

    cout << n - ans;

OJ练习

379. 捉迷藏 - AcWing题库

这道题目的最大藏身点数目就是给定的有向无环图的最小路径可重复覆盖数目。

设最大藏身点集合为hide,最小路径可重复覆盖为path,往证hide = path:

显然藏身点不能在同一条路径上,而path包含了所有的点,所以每条path最多选一个藏身点,故|hide| <= |path|

如果我们能在path上构造出|path|个藏身点,那么就得证了,因为|hide|是最大藏身点数目,而最大又不超过|path|。

求path很容易,我们即然能求出最小覆盖,自然能还原出路径:

  1. 对于每条path的终点即为拆点二分图非匹配点,这个显然很好求
  2. 非匹配点x的右部拆点为x + n,那么通过match[x + n]可以找到x在path的邻接点y,同样的方法一直往下进行,可以还原出path

问题在于如何从每条path上选出一个藏身点:

  1. 记path终点集合为E,从E发出的边往下走,访问到的节点集合为next(E)
  2. 若E ∩ next(E) = Ø,即藏身点之间无路径,那么E 可以作为 hide
  3. 否则,对于E ∩ next(E)中的 每个节点e,沿着路径往回走,总能找到e’ ∉ next(E),否则path数目会减少,和最小覆盖矛盾
  4. 找到e’后删除e,再重复3,直到E ∩ next(E) = Ø,此时E即为符合条件的hide

证毕

AC代码如下:

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cmath>
#include <functional>
using namespace std;
#define int long long
#define N 205

int n, m, match[N]{0}, ans = 0;
bool vis[N], g[N][N]{0};

bool dfs(int u)
{
    for (int v = 1; v <= n; v++)
        if (!vis[v] && g[u][v])
        {
            vis[v] = true;
            if (!match[v] || dfs(match[v]))
            {
                match[v] = u;
                return true;
            }
        }
    return false;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    //freopen("in.txt", "r", stdin);

    cin >> n >> m;
    for (int i = 0, a, b; i < m; i++)
    {
        cin >> a >> b;
        g[a][b] = true;
    }
    for (int i = 1; i <= n; i++)
        g[i][i] = true;

    // 传递闭包
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] |= g[i][k] && g[k][j];

    for (int i = 1; i <= n; i++)
        g[i][i] = false;

    for (int i = 1; i <= n; i++)
        memset(vis, 0, sizeof(vis)), ans += dfs(i);

    cout << n - ans;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1400981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker使用及部署python项目

一、准备项目 ​ 我写的是一个爬取某ppt网站的代码&#xff0c;就一个ppt1.py是爬虫&#xff0c;然后&#xff0c;ppts是存放下载的ppt的 二、准备requirement.txt文件 这个是需要哪些python库支持&#xff0c;写好 ​ 三、准备Dockerfile文件 需要一个名为Dockerfile的文件&…

基于SpringBoot的船运物流管理系统

文章目录 项目介绍主要功能部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &#x1f345;文末获取…

计算机组成原理04:一位乘法

原码的一位乘法是基于加法设计的。回想我们在竖式计算乘法时&#xff0c;都是通过一个数与另外一个数的另外一位相乘&#xff0c;最后相加得到结果。计算机计算原码一位乘法也是一样的原理。这里就涉及到计算时一个非常重要的操作&#xff1a;数据移位。 原码乘法问题分析 需…

13.浮动面板(PaletteSet)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET FrameWork4.5、ObjectArx 2016 64bit、Entity Framework 6. 在CAD中进行通用组件开发或常驻界面的控件开发时&#xff0c;可使用PaletteSet作为停靠面板&#xff0c;然后将自己的空间放入其中。 1.示例 SearchRe…

React初探:从环境搭建到Hooks应用全解析

React初探&#xff1a;从环境搭建到Hooks应用全解析 一、React介绍 1、React是什么 React是由Facebook开发的一款用于构建用户界面的JavaScript库。它主要用于构建单页面应用中的UI组件&#xff0c;通过组件化的方式让开发者能够更轻松地构建可维护且高效的用户界面。 Reac…

初始RabbitMQ(入门篇)

消息队列(MQ) 本质上就是一个队列,一个先进先出的队列,队列中存放的内容是message(消息),是一种跨进程的通信机制,用于上下游传递消息, 为什么使用MQ: 削峰填谷: MQ可以很好的做一个缓冲机制,例如在一个系统中有A和B两个应用,A是接收用户的请求的,然后A调用B进行处理. 这时…

前端基础面试题大全

一、Vue 文章目录 一、Vue1、vue 修改数据页面不重新渲染**数组/对象的响应式 &#xff0c;vue 里面是怎么处理的&#xff1f;** 2、生命周期Vue 生命周期都有哪些&#xff1f;父子组件生命周期执行顺序 3、watch 和 computed 的区别4、组件通信&#xff08;组件间传值&#xf…

gin中间件篇

1. 全局中间件 所有请求都经过此中间件 package mainimport ("fmt""time""github.com/gin-gonic/gin" )// 定义中间 func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t : time.Now()fmt.Println("中间件开始执行了&quo…

力扣每日一题---1547. 切棍子的最小成本

//当我们将棍子分段之后&#xff0c;我们是不是想到了怎么组合这些棍子 //并且这些棍子有一个性质就是只能与相邻的进行组合 //暴力搜索的话复杂度很高 //在思考暴力搜索的时候&#xff0c;我们发现一个规律 //比如棍子长度1 2 1 1 2 //那么与最后一个2组合的棍子有&#xff0c…

Vue3+ElementUIPlus颜色选择器,Ruoyi框架动态替换图片

需求为&#xff0c;需要动态的替换头部和底部图片的颜色&#xff0c;通过固定的颜色 要实现可以动态的通过颜色&#xff0c;去替换的效果。 一、通过将选择的颜色&#xff0c;通过Vuex来进行一个存储&#xff0c;用户后续的使用 <el-form-item label"顶部底部背景&quo…

LabVIEW滚动轴承故障在线监测

展示了如何将LabVIEW开发出一种有效的滚动轴承故障在线监测系统。介绍了该系统的开发过程、工作原理及其在实际应用中的效果。该系统成功地应用于对滚动轴承故障的早期诊断&#xff0c;提高了故障检测的准确性和效率。 滚动轴承在工作过程中会产生复杂的振动信号&#xff0c;包…

19. JDK8以后的时间类(Date类、日期格式化类、日历类、工具类)

JDK8以后的时间类 Date类1. ZoneID类1.1 方法1.2 代码示例 2. Instant类2.1 方法2.2 代码示例 3. ZoneDateTime类3.1 方法3.2 代码示例 日期格式化类1. DateTimeFormatter类1.1 方法1.2 代码示例 日历类1. LocalDate类1.1 方法1.2 代码示例 2. LocalTime类2.1 方法2.2 代码示例…

Java设计模式详解-更新中

收藏和关注的同时&#xff0c;请也关注 公众号 “IT技术馆” 各位大家好&#xff0c;从今天开始&#xff0c;作者开始整理 《JAVA软件设计模式&#xff08;GOF&#xff09;》 专栏。请各位多多关注&#xff01; 该专栏是根据作者的技术经验和设计模式的了解&#xff0c;进行详…

ospf综合实验配置

实验规则如上&#xff1a; 划分ip地址&#xff1a;七个骨干&#xff0c;五个环回 首先划分两个ip&#xff0c;一个给骨干&#xff0c;一个给环回 192.168.1.0/24 -- 1.划分七个骨干网络- 2.划分5个环回网络- 192.168.1.0/25--骨干-----192.168.1.0/28 192.168.1.0 000 0…

numpy中数组的操作

目录 一&#xff1a;数组的属性 二&#xff1a;翻转数组 三&#xff1a;数组的计算 一&#xff1a;数组的属性 NumPy 数组&#xff08;通常称为 ndarray&#xff09;有许多有用的属性&#xff0c;这些属性可以帮助你了解数组的各个方面。以下是一些主要的属性&#xff1a; …

java idea 中的 Scratches and Consoles

IDEA 中&#xff0c;"Scratches and Consoles" 是一个用于临时代码编辑和交互式开发的工具窗口&#xff0c;作用如下&#xff1a;Scratches&#xff08;草稿&#xff09;&#xff1a;Scratches 是一个用于临时编写和运行代码片段的工具&#xff0c;你可以在其中创建临…

如何创建vite项目!

vite 官网&#xff1a;vite是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验 网络&#xff1a;vite是一个静态服务器&#xff0c;也可以说是一个开发的构建工具 它的目标就是提供快速的开发体验和性能优化 vite优点与缺点 Vite 优点Vite 缺点开发服务器比 Webp…

一文掌握SpringBoot注解之@Cacheable 知识文集(1)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

echarts-wordcloud词云

echarts-wordcloud是基于echarts的一个插件&#xff0c;所以我们要首先安装echarts包&#xff0c;然后再安装echarts-wordcloud的包&#xff0c;这里我的练习项目安装的版本&#xff1b;当然&#xff0c;你可以随意安装你需要的版本&#xff1b; “echarts”: “^5.3.3”, “ec…

一文搞懂SECS/GEM(二)

继《一文搞懂SECS/GEM&#xff08;一&#xff09;》继续补充 这里写目录标题 HSMS2种连接模式&#xff08;Connect Mode&#xff09;6类消息消息交换过程Select ProcedureData ProcedureDeselect ProcedureLinktest ProcedureSeparate ProcedureReject Procedure 4种状态状态转…