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

最小路径覆盖就是给定一张DAG,要求用尽量少的不相交的简单路径,覆盖有向无环图的所有顶点。

有定理:顶点数-路径数=被覆盖的边数。

要理解的话可以从两个方向:

  • 假设DAG已经被n条路径覆盖,那么任意一条路径又有 顶点数-1=边数。那么对所有路径等式两边求和,每条路径的顶点数之和=所有点数,-1的和=路径数,每条路径的边数之和=被覆盖的边数。。这样上面的定理就成立了。

  • 还有一种方法,我们要先引入二分图

我们把原图中的点拆成出点(边从该点出)和入点(边从该点入),即原图点x在二分图中对应出点x,入点x+n。

原图中的边(x,y)对应二分图中的(x,y+n)。我们每次选择路径,因为边不能相交,所以对于一个点,只有一个入和一个出,这显然是一个匹配问题。

选择的边(x,y)相当于从源点s到x,从x到y+n,从y+n到汇点t有了单位流量。

特别的,如果一个点是路径的终点,那么他没有出度,即该点二分匹配失败。

可以显然得出,最后匹配失败的点数就是路径数。

因为源点相连的点一定有n个,所以有 顶点数-路径数=二分图最大匹配。

且由上述概念得二分图最大匹配即为路径选择的边数(被覆盖的边数)

所以我们把题目给的点拆开跑最大流就可以啦!

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define full(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
inline int lowbit(int x){ return x & (-x); }
inline int read(){
int X = 0, w = 0; char ch = 0;
while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
return w ? -X : X;
}
inline int gcd(int a, int b){ return a % b ? gcd(b, a % b) : b; }
inline int lcm(int a, int b){ return a / gcd(a, b) * b; }
template<typename T>
inline T max(T x, T y, T z){ return max(max(x, y), z); }
template<typename T>
inline T min(T x, T y, T z){ return min(min(x, y), z); }
template<typename A, typename B, typename C>
inline A fpow(A x, B p, C lyd){
A ans = 1;
for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
return ans;
}
const int N = 505;
const int M = 6005;
int n, m, cnt, head[N], depth[N], to[N], vis[N];
struct Edge { int v, next, f; } edge[M<<5]; void addEdge(int a, int b, int f){
edge[cnt].v = b, edge[cnt].f = f, edge[cnt].next = head[a], head[a] = cnt ++;
edge[cnt].v = a, edge[cnt].f = 0, edge[cnt].next = head[b], head[b] = cnt ++;
} bool bfs(){
full(depth, 0);
queue<int> q;
depth[0] = 1, q.push(0);
while(!q.empty()){
int s = q.front(); q.pop();
for(int i = head[s]; i != -1; i = edge[i].next){
int u = edge[i].v;
if(!depth[u] && edge[i].f > 0){
depth[u] = depth[s] + 1;
q.push(u);
}
}
}
return depth[2 * n + 1] != 0;
} int dfs(int s, int a){
if(s == 2 * n + 1) return a;
int flow = 0;
for(int i = head[s]; i != -1; i = edge[i].next){
int u = edge[i].v;
if(depth[u] == depth[s] + 1 && edge[i].f > 0){
int k = dfs(u, min(a, edge[i].f));
if(k > 0){
flow += k, a -= k, edge[i].f -= k, edge[i^1].f += k, to[s] = u;
if(s != 0) vis[u - n] = true;
}
}
if(!a) break;
}
if(a) depth[s] = -1;
return flow;
} int dinic(){
int ret = 0;
while(bfs()){
ret += dfs(0, INF);
}
return ret;
} int main(){ full(head, -1);
n = read(), m = read();
for(int i = 1; i <= n; i ++)
addEdge(0, i, 1), addEdge(i + n, 2 * n + 1, 1);
for(int i = 0; i < m; i ++){
int u = read(), v = read();
addEdge(u, v + n, 1);
}
int ans = n - dinic();
for(int i = 1; i <= n; i ++){
if(!vis[i]){
int cur = i;
printf("%d ", cur);
while(to[cur] != 2 * n + 1 && to[cur] != 0){
printf("%d ", to[cur] - n), cur = to[cur] - n;
}
puts("");
}
}
printf("%d\n", ans);
return 0;
}

洛谷P2764 最小路径覆盖问题的更多相关文章

  1. 洛谷 P2764 最小路径覆盖问题 解题报告

    P2764 最小路径覆盖问题 问题描述: 给定有向图\(G=(V,E)\).设\(P\) 是\(G\) 的一个简单路(顶点不相交)的集合.如果\(V\) 中每个顶点恰好在\(P\) 的一条路上,则称\ ...

  2. 洛谷 P2764 最小路径覆盖问题【最大流+拆点+路径输出】

    题目链接:https://www.luogu.org/problemnew/show/P2764 题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V ...

  3. 【刷题】洛谷 P2764 最小路径覆盖问题

    题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...

  4. 洛谷P2764 最小路径覆盖问题(最大流)

    传送门 先说做法:把原图拆成一个二分图,每一个点被拆成$A_i,B_i$,若原图中存在边$(u,v)$,则连边$(A_u,B_v)$,然后$S$对所有$A$连边,所有$B$对$T$连边,然后跑一个最大 ...

  5. 洛谷 P2764 最小路径覆盖问题【匈牙利算法】

    经典二分图匹配问题.把每个点拆成两个,对于原图中的每一条边(i,j)连接(i,j+n),最小路径覆盖就是点数n-二分图最大匹配.方案直接顺着匹配dsf.. #include<iostream&g ...

  6. 洛谷 P2764(最小路径覆盖=节点数-最大匹配)

    给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别 ...

  7. 洛谷P2764 最小路径覆盖问题(二分图)

    题意 给出一张有向无环图,求出用最少的路径覆盖整张图,要求路径在定点处不相交 输出方案 Sol 定理:路径覆盖 = 定点数 - 二分图最大匹配数 直接上匈牙利 输出方案的话就不断的从一个点跳匹配边 # ...

  8. 洛谷 [P2764]最小路径覆盖问题

    二分图应用模版 #include <iostream> #include <cstdio> #include <algorithm> #include <cs ...

  9. 洛谷-p2764(最小路径覆盖)(网络流24题)

    #include<iostream> #include<algorithm> #include<queue> #include<cstring> #in ...

随机推荐

  1. TensorFlow 使用变量共享

    参考: https://www.tensorflow.org/programmers_guide/variable_scope 举例说明 TensorFlow中的变量一般就是模型的参数.当模型复杂的时 ...

  2. 基于 HTML5 的工业互联网 3D 可视化应用

    工业企业中生产线处于高速运转,由工业设备所产生.采集和处理的数据量远大于企业中计算机和人工产生的数据,生产线的高速运转则对数据的实时性要求也更高.破解这些大数据就是企业在新一轮制造革命中赢得竞争力的钥 ...

  3. SNMP 获取交换机端口相关信息

    原文地址:https://blog.csdn.net/ysdaniel/article/details/37927541 我们想用snmpwalk查看网络设备的端口,MIB库中相关定义的信息如下: [ ...

  4. H5 61-浮动元素贴靠现象

    61-浮动元素贴靠现象 <!DOCTYPE html><html lang="en"><head> <meta charset=" ...

  5. H5 video标签的第二种格式

    36-video标签的第二种格式 2.第二种格式存在的意义: 由于视频数据非常非常的重要, 所以五大浏览器厂商都不愿意支持别人的视频格式, 所以导致了没有一种视频格式是所有浏览器都支持的 这个时候W3 ...

  6. java高精度学习笔记

    高精度基本用法 valueOf(parament)     将参数转换为指定的类型 add()   相加   subtract() 相减    multiply()  相乘    divide()  ...

  7. 软件工程练习:模块化,单元测试,回归测试,TDD

    这是<构建之法>实战教学的一部分.适合作为同学们的第二个程序作业. 第一个程序作业: 请看 “概论” 一章的练习,或者老师的题目,例如这个. 作业要求: 软件工程的作业越来越有意思了, 我 ...

  8. 每周分享之JS数组的使用

    数组,一堆数字归为一组,就是一个数组,一堆对象放在一个组里,也是一个数组,概念很容易懂,说白了就是一个有限集合. JS数组的语法无法两种,插入和移除(语法自行科普).用处挺常见的,既然数组是一个集合, ...

  9. git reset的用法

    git reset三个选项 --mix,--hard,--soft 数据 针对每个选项都是操作这个文件. [root@centos demo]# git init Initialized empty ...

  10. selenium处理alert弹出框

    import time from selenium import webdriver driver =webdriver.Chrome(r"D:\工具包\chromedriver.exe&q ...