P2764 最小路径覆盖问题 网络流输出方案
发布日期:2021-06-28 19:59:53
浏览次数:2
分类:技术文章
本文共 3240 字,大约阅读时间需要 10 分钟。
题意:
求DAG的最小路径覆盖并输出方案。所谓最小路径覆盖是指,将原图分为若干条路径,任意两条路径不能有公共点,要使路径数量尽可能少。最小路径覆盖,emmm好像跟最少边覆盖差不多。但这俩确实不是一个东西。
最小边覆盖:边数最小的边覆盖集称为最小边覆盖,通俗地讲,就是极小边覆盖中的最小的一个集合。
最小边覆盖在二分图中的应用:最小边覆盖 = 最大独立集 = n - 最大匹配,这个是二分图上的一个性质。 两个很小的点:必须要求原图为二分图;边覆盖集中可能有多条边覆盖同一个顶点,其实可以这条边说是为了覆盖另一个顶点而存在的。
继续说最小路径问题:
先说结论,原图中有 N N N个点,把每个点拆成两个点,记为 X i , Y i X_i,Y_i Xi,Yi,对于任意一条原图中的边 ( u , v ) (u,v) (u,v),连接 ( X u , Y v ) (X_u,Y_v) (Xu,Yv)(有向边)。很容易发现,左半部对应着每条边的起点,右半部对应着终点。然后以 X X X为左半部, Y Y Y为右半部做二分图的最大匹配,得到答案 a n s ans ans,则最小路径覆盖就是 N − a n s N-ans N−ans。为什么是N-ans呢?
我们先假设有N个单独的点,那么最大匹配书即为0,那么答案就是N。 如果每匹配成功一条边,那么就意味着原图有一个点融入了另一条路径,这样的话我们就可以少开辟一条简单路径去遍历这个点(也就是最小路径-1)。匹配成功多少次,那么最小路径覆盖则会-1,所以问题就变成了二分图最大匹配问题。再说如何把方案输出?
我们可以用网络流来跑这个二分图,我们知道跑完网络流的图是会被改变的容量不变,流量会变。假设我们发现某条边的容量==流量并且容量不为0,则代表这条边有流量经过,那么我们则可以把这个u,v这两个点做一下标记。用两个数组pre,lst ,pre代表这个点之前是有哪个点流过来的,lst代表这个点之后会流向哪个点。
最后遍历N个点,如果遇到某点的pre==0(意味着这个点是某条简单路径的起点),那么就顺着这个点输出这个简单路径即可。
代码:
#includeusing namespace std;const int maxn = 1010;const int maxm = 1e6+10;struct edge{ long long to, next, cap, flow; // 容量 和 流量}e[maxm << 1]; int head[maxn], tot;int n, m, s, t, d[maxn], cur[maxn]; // d[]分层,cur[]弧优化inline void addedge(int u, int v, int w) { e[++tot] = { v, head[u], w, 0}, head[u] = tot; e[++tot] = { u, head[v], 0, 0}, head[v] = tot;}inline bool bfs() { // 分层 t-s路径优化 memset(d, 0x3f, sizeof d); queue q; q.push(t); d[t] = 0; while(!q.empty()) { int x = q.front(); q.pop(); for(int i = head[x]; ~i; i = e[i].next) { int j = e[i].to; if (d[j] == d[0] && e[i^1].cap > e[i^1].flow) { d[j] = d[x] + 1; if (j == s) return true; q.push(j); } } } return false;}long long dfs(int x, long long flow, long long used = 0) { //used多路增广 if (x == t) return flow; //for(int i = cur[x]; ~i && flow != used; i = e[ cur[x] = i ].next) { // 当前弧优化 for(int i = head[x]; ~i && flow != used; i = e[i].next) { long long j = e[i].to, f; if (d[x] - 1 == d[j] && e[i].cap > e[i].flow) { f = dfs(j, min(e[i].cap-e[i].flow, flow-used)); if (f) e[i].flow += f, e[i^1].flow -= f, used += f; } } if (!used) d[x] = 0; // 剪枝优化 return used;}int dinic(){ long long maxflow = 0; while(bfs()) { // 每次dfs分层都能找到多条增广路 //memcpy(cur, head, sizeof head); maxflow += dfs(s, INT_MAX); } return n-maxflow;}void init(){ memset(head,-1,sizeof head); tot=-1;}int pre[maxn],lst[maxn];int main() { //freopen("1.in","r",stdin); init(); cin>>n>>m; for(int i=0;i >x>>y; addedge(x,y+n,1); } s=0,t=2*n+2; for(int i=1;i<=n;i++){ addedge(s,i,1); addedge(i+n,t,1); } int ans=dinic(); for(int i=1;i<=n;i++){ for(int j=head[i];~j;j=e[j].next){ int v=e[j].to; if(e[j].cap-e[j].flow==0&&e[j].flow==1){ pre[v-n]=i; lst[i]=v-n; } } } for(int i=1;i<=n;i++){ if(!pre[i]){ int u=i; while(lst[u]!=0){ cout< <<" "; u=lst[u]; } cout<<
转载地址:https://blog.csdn.net/xxxxxiao123/article/details/116614136 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月26日 04时18分04秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
【基础+实战】JVM原理及优化系列之一:JVM体系结构
2019-04-29
【基础+实战】JVM原理及优化系列之二:JVM内存管理
2019-04-29
【基础+实战】JVM原理及优化系列之三:JVM垃圾收集器
2019-04-29
【基础+实战】JVM原理及优化系列之四:JVM参数说明
2019-04-29
【基础+实战】JVM原理及优化系列之五:JVM默认设置
2019-04-29
【基础+实战】JVM原理及优化系列之六:JVM主要调优参数
2019-04-29
【基础+实战】JVM原理及优化系列之十:JVM内存泄漏专题实战
2019-04-29
Redis高可用架构 (redis主从+sentinel)
2019-04-29
【重磅推出】性能提升100倍的性能测试监控优化方法
2019-04-29
Spring cloud微服务框架简介
2019-04-29
【实用】Redis各种存储结构使用场景
2019-04-29
【实用】Redis高级功能
2019-04-29
DevOps八荣八耻了解下,哈哈~
2019-04-29
API Gateway(API网关)介绍
2019-04-29
【免费】少儿编程社区,Scratch中文社区,少儿编程学习交流平台上线~~~
2019-04-29
JavaMail关于使用qq企业邮箱发邮件踩过的坑
2019-04-29
log4j2异步发送error日志邮件配置
2019-04-29
redis setnx解决定时任务多节点部署并发问题(分布式锁)
2019-04-29
spring boot使用redis解决session双机问题
2019-04-29