网络流二十四题之P2764 最小路径覆盖问题
题目描述
给定有向图 G=(V,E)G=(V,E) 。设 PP 是 GG 的一个简单路(顶点不相交)的集合。如果 VV 中每个定点恰好在PP的一条路上,则称 PP 是 GG 的一个路径覆盖。PP中路径可以从 VV 的任何一个定点开始,长度也是任意的,特别地,可以为 00 。GG 的最小路径覆盖是 GG 所含路径条数最少的路径覆盖。设计一个有效算法求一个 GAP (有向无环图) GG 的最小路径覆盖。
提示:设 V=\{1,2,...,n\}V={1,2,...,n} ,构造网络 G_1=\{V_1,E_1\}G1={V1,E1} 如下:
V_1=\{x_0,x_1,...,x_n\}\cup\{y_0,y_1,...,y_n\}V1={x0,x1,...,xn}∪{y0,y1,...,yn}
E_1=\{(x_0,x_i):i\in V\}\cup\{(y_i,y_0):i\in V\}\cup\{(x_i,y_j):(i,j)\in E\}E1={(x0,xi):i∈V}∪{(yi,y0):i∈V}∪{(xi,yj):(i,j)∈E}
每条边的容量均为 11 ,求网络 G_1G1 的 (x_0,y_0)(x0,y0) 最大流。
输入输出格式
输入格式:
第一行有 22 个正整数 nn 和 mm 。 nn 是给定\text{GAP}GAP(有向无环图) GG 的顶点数, mm 是 GG 的边数。接下来的 mm行,每行有两个正整数 ii 和 jj 表示一条有向边 (i,j)(i,j)。
输出格式:
从第1 行开始,每行输出一条路径。文件的最后一行是最少路径数。
输入输出样例
11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11
1 4 7 10 11
2 5 8
3 6 9
3
说明
1\leq n\leq 150,1\leq m\leq 60001≤n≤150,1≤m≤6000
由@FlierKing提供SPJ
这个题目有点难读,我读了挺久的,这个题目是让你找最少的路径使得所有的路径合在一起可以覆盖了整个图。
没有最少自然可以是n,就是所有点自成一条路径。
拆分题目有点难写,我开始根本就没有什么思路,后来看到二分图匹配有一个关于这个的定理,
DAG图的最小路径覆盖数 = 节点数 - 二分图的最大匹配
所以这个就可以求解,然后这个转化成二分图,就需要把一个点拆成两个点,然后再合并。
说多了也没用,自己看代码理解吧。
我开始看题解,以为只能用链式前向星建图,真的要哭了,不太会用,后来发现也可以用vector
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
#include <vector>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = ;
int level[maxn], iter[maxn];
int head[maxn];
int n,m,s = , t = ;
struct node
{
int to, cap,flow;
int nex;
}exa[maxn<<];
int cnt = ;
void add(int u,int v,int c)
{
exa[++cnt].to = v;
exa[cnt].cap = c;
exa[cnt].flow = ;
exa[cnt].nex = head[u];
head[u] = cnt;
//printf("%d %d %d %d %d\n",cnt,u, exa[cnt].to, exa[cnt].cap, exa[cnt].nex); exa[++cnt].to = u;
exa[cnt].cap = ;
exa[cnt].flow = ;
exa[cnt].nex = head[v];
head[v] = cnt;
//printf("%d %d %d %d %d\n",cnt,v, exa[cnt].to, exa[cnt].cap, exa[cnt].nex);
} void bfs(int s)
{
queue<int>que;
que.push(s);
memset(level, -, sizeof(level));
level[s] = ;
while(!que.empty())
{
int u = que.front(); que.pop();
//printf("%d %d %d\n", u,head[u],exa[head[u]].nex);
for(int i=head[u];i!=-;i=exa[i].nex)
{
node &now = exa[i];
//printf("%d %d %d\n", i, exa[i].to, exa[i].nex);
if(level[now.to]<&&now.cap>now.flow)
{
level[now.to] = level[u] + ;
que.push(now.to);
}
}
}
}
int tag[maxn], to[maxn]; int dfs(int u,int v,int f)
{
if (u == v) return f;
for(int &i=iter[u];i!=-;i=exa[i].nex)
{
node &now = exa[i];
if(now.cap>now.flow&&level[now.to]>level[u])
{
int d = dfs(now.to, v, min(f, now.cap - now.flow));
if(d>)
{
to[u] = now.to;
if (u != s) tag[now.to - n] = ;
now.flow += d;
exa[i ^ ].flow -= d;
return d;
}
}
}
return ;
}
void init()
{
memset(head, -, sizeof(head));
}
int dinic(int s,int t)
{
int flow = ;
while()
{
bfs(s);
if (level[t] < ) break;
for (int i = s; i <= t; i++) iter[i] = head[i];
int f;
while ((f = dfs(s, t, inf)) > ) flow += f; }
for(int i=;i<=n;i++)
{
if(!tag[i])
{
int now = i;
printf("%d ", now);
while(to[now]&&to[now]!=t)
{
printf("%d ", to[now] - n);
now = to[now] - n;
}
printf("\n");
}
}
return flow;
} int main()
{
init();
cin >> n >> m;
t = * n + ;
for (int i = ; i <= n; i++) add(s, i, );
for (int i = ; i <= n; i++) add(i+n, t, );
for(int i=;i<=m;i++)
{
int a, b;
cin >> a >> b;
add(a, b+n, );
}
int ans = dinic(s, t);
printf("%d\n",n - ans);
return ;
}
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
#include <vector>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = ;
int level[maxn], iter[maxn];
int n,m,s = , t = ;
struct node
{
int from,to, cap,flow;
node(int from=,int to=,int cap=,int flow=):from(from),to(to),cap(cap),flow(flow){} };
vector<node>e;
vector<int>G[maxn];
void add(int u,int v,int c)
{
e.push_back(node(u, v, c, ));
e.push_back(node(v, u, , ));
int len = e.size();
G[u].push_back(len - );
G[v].push_back(len - );
} void bfs(int s)
{
queue<int>que;
que.push(s);
memset(level, -, sizeof(level));
level[s] = ;
while(!que.empty())
{
int u = que.front(); que.pop();
for(int i=;i<G[u].size();i++)
{
node &now = e[G[u][i]];
if(level[now.to]<&&now.cap>now.flow)
{
level[now.to] = level[u] + ;
que.push(now.to);
}
}
}
}
int tag[maxn], to[maxn]; int dfs(int u,int v,int f)
{
if (u == v) return f;
for(int &i=iter[u];i<G[u].size();i++)
{
node &now = e[G[u][i]];
if(now.cap>now.flow&&level[now.to]>level[u])
{
int d = dfs(now.to, v, min(f, now.cap - now.flow));
if(d>)
{
to[u] = now.to;
// printf("%d %d\n", u, to[u]);
if (u != s) tag[now.to - n] = ;
now.flow += d;
e[G[u][i] ^ ].flow -= d;
return d;
}
}
}
return ;
}
void init()
{
for (int i = ; i <= n + ; i++) G[i].clear();
e.clear();
}
int dinic(int s,int t)
{
int flow = ;
while()
{
bfs(s);
if (level[t] < ) break;
memset(iter, , sizeof(iter));
int f;
while ((f = dfs(s, t, inf)) > ) flow += f; }
//cout << endl;
for(int i=;i<=n;i++)
{
if(!tag[i])
{
int now = i;
printf("%d ", now);
while(to[now]&&to[now]!=t)
{
printf("%d ", to[now] - n);
now = to[now] - n;
}
printf("\n");
}
}
return flow;
} int main()
{
init();
cin >> n >> m;
t = * n + ;
for (int i = ; i <= n; i++) add(s, i, );
for (int i = ; i <= n; i++) add(i+n, t, );
for(int i=;i<=m;i++)
{
int a, b;
cin >> a >> b;
add(a, b+n, );
}
int ans = dinic(s, t);
printf("%d\n",n - ans);
return ;
}
网络流二十四题之P2764 最小路径覆盖问题的更多相关文章
- 网络流二十四题,题解summary
没有全部写完,有几题以后再补吧. 第一题:最简单的:飞行员配对方案问题 讲讲这个题目为什么可以用网络流? 因为这个题目是要进行两两之间的匹配,这个就可以想到用二分图匹配,二分图匹配又可以用网络流写. ...
- P4013 数字梯形问题 网络流二十四题
P4013 数字梯形问题 题目描述 给定一个由 nn 行数字组成的数字梯形如下图所示. 梯形的第一行有 m 个数字.从梯形的顶部的 m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形 ...
- P2765 魔术球问题 网络流二十四题重温
P2765 魔术球问题 知识点::最小点覆盖 这个题目要拆点,这个不是因为每一个球只能用一次,而是因为我们要求最小点覆盖,所以要拆点来写. 思路: 首先拆点,然后就是开始建边,因为建边的条件是要求他们 ...
- P2764 最小路径覆盖问题 网络流重温
P2764 最小路径覆盖问题 这个题目之前第一次做的时候感觉很难,现在好多了,主要是二分图定理不太记得了,二分图定理 知道这个之后就很好写了,首先我们对每一个点进行拆点,拆完点之后就是跑最大流,求出最 ...
- Luogu P2764 最小路径覆盖问题(二分图匹配)
P2764 最小路径覆盖问题 题面 题目描述 «问题描述: 给定有向图 \(G=(V,E)\) .设 \(P\) 是 \(G\) 的一个简单路(顶点不相交)的集合.如果 \(V\) 中每个顶点恰好在 ...
- 洛谷 P2764 最小路径覆盖问题 解题报告
P2764 最小路径覆盖问题 问题描述: 给定有向图\(G=(V,E)\).设\(P\) 是\(G\) 的一个简单路(顶点不相交)的集合.如果\(V\) 中每个顶点恰好在\(P\) 的一条路上,则称\ ...
- P2764 最小路径覆盖问题(网络流24题之一)
题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...
- 【刷题】洛谷 P2764 最小路径覆盖问题
题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...
- P2764 最小路径覆盖问题
题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...
随机推荐
- Python爬虫入门教程 39-100 天津市科技计划项目成果库数据抓取 scrapy
爬前叨叨 缘由 今天本来没有打算抓取这个网站的,无意中看到某个微信群有人问了一嘴这个网站,想看一下有什么特别复杂的地方,一顿操作下来,发现这个网站除了卡慢,经常自己宕机以外,好像还真没有什么特殊的.. ...
- Kestrel.Transport.Sockets分析与使用
相信大家都清楚asp core有着非常出色的性能,它出色的性能也源于网络服务模块Kestrel:在techempower测试中Kestrel基础核心达到了700万级别的RPS吞吐能力,具备这样的能力那 ...
- 《HelloGitHub月刊》第 09 期
<HelloGitHub>第 09 期 兴趣是最好的老师,<HelloGitHub>就是帮你找到兴趣! 前言 转眼就到年底了,月刊做到了第09期,感谢大家一路的支持和帮助
- Python编写守护进程程序
Python编写守护进程程序思路 1. fork子进程,父进程退出通常,我们执行服务端程序的时候都会通过终端连接到服务器,成功连接后会加载shell环境,终端和shell都是进程,shell进程是终端 ...
- 【转载】CentOS 7部署ASP.NET Core应用程序
看了几篇大牛写的关于Linux部署ASP.NET Core程序的文章,今天来实战演练一下.2017年最后一个工作日,提前预祝大家伙元旦快乐.不扯淡,直接进入正题.您有任何问题请在评论区留言. 1.环境 ...
- JAVA开发知识之Java的线程
目录 Java多线程讲解 一丶多线程简介 1.进程的概念 2.线程概念 3.Java中多线程Thread类 二丶多线程的创建 1.继承Thread类.重写run方法. 2.实现Runalbe接口. 三 ...
- [五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的
Launcher启动类 本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的 不过源码其实比较简单,接下来简单介绍一下 我们先从启动类说起 有一个Lau ...
- SpringCloud系列——Zuul 动态路由
前言 Zuul 是在Spring Cloud Netflix平台上提供动态路由,监控,弹性,安全等边缘服务的框架,是Netflix基于jvm的路由器和服务器端负载均衡器,相当于是设备和 Netflix ...
- JS闭包作用域解析
什么是闭包? 简单理解,当在一个函数的外部访问函数内部定义的变量的时候就会形成一个闭包,由这个理解可以知道,当一个函数执行完成的时候,一般情况下,其作用域会被销毁,其内部定义的变量也会变得不可访问,所 ...
- Eclipse Ctrl+Space 无法工作的问题
Window->preference->Keys 然后找到content Assist,然后重新设置为自己想要的Short cut 就好了