题目描述

给定有向图 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 行开始,每行输出一条路径。文件的最后一行是最少路径数。

输入输出样例

输入样例#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: 复制

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 最小路径覆盖问题的更多相关文章

  1. 网络流二十四题,题解summary

    没有全部写完,有几题以后再补吧. 第一题:最简单的:飞行员配对方案问题 讲讲这个题目为什么可以用网络流? 因为这个题目是要进行两两之间的匹配,这个就可以想到用二分图匹配,二分图匹配又可以用网络流写. ...

  2. P4013 数字梯形问题 网络流二十四题

    P4013 数字梯形问题 题目描述 给定一个由 nn 行数字组成的数字梯形如下图所示. 梯形的第一行有 m 个数字.从梯形的顶部的 m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形 ...

  3. P2765 魔术球问题 网络流二十四题重温

    P2765 魔术球问题 知识点::最小点覆盖 这个题目要拆点,这个不是因为每一个球只能用一次,而是因为我们要求最小点覆盖,所以要拆点来写. 思路: 首先拆点,然后就是开始建边,因为建边的条件是要求他们 ...

  4. P2764 最小路径覆盖问题 网络流重温

    P2764 最小路径覆盖问题 这个题目之前第一次做的时候感觉很难,现在好多了,主要是二分图定理不太记得了,二分图定理 知道这个之后就很好写了,首先我们对每一个点进行拆点,拆完点之后就是跑最大流,求出最 ...

  5. Luogu P2764 最小路径覆盖问题(二分图匹配)

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

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

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

  7. P2764 最小路径覆盖问题(网络流24题之一)

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

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

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

  9. P2764 最小路径覆盖问题

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

随机推荐

  1. 简单上手nodejs调用c++(c++和js的混合编程)

    因为项目的原因,最近经常使用node.js搭RESTful接口. 性能还是很不错啦,感觉比Spring Boot之类的要快.而且在不错的性能之外,只要程序结构组织好,别让太多的回调把程序结构搞乱,整体 ...

  2. Java的类加载器种类(双亲委派)

    Java类加载器采用双亲委派模型: 1.启动类加载器:这个类加载器负责放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别 ...

  3. SmartSql = Dapper + MyBatis + Cache(Memory | Redis) + ZooKeeper + R/W Splitting + ......

    SmartSql Why 拥抱 跨平台 DotNet Core,是时候了. 高性能.高生产力,超轻量级的ORM.156kb (Dapper:168kb) So SmartSql TargetFrame ...

  4. spring原理案例-基本项目搭建 03 创建工程运行测试 spring ioc原理实例示例

    下面开始项目的搭建 使用 Java EE - Eclipse 新建一 Dynamic Web Project Target Runtime 选 Apache Tomcat 7.0(不要选 Apache ...

  5. #6 ipdb模块源代码解读

    前言 好久不见,大家最近可好

  6. Perl信号处理

    本文关于Perl信号处理的内容主体来自于<Pro Perl>的第21章. 信号处理 操作系统可以通过信号(signal)处理机制来实现一些功能:程序注册好待监视的信号处理机制,在程序运行过 ...

  7. golang判断文件是否存在

    判断一个文件是否存在是一个相当常见的需求,在golang中也有多种方案实现这一功能. 现在我们介绍其中两种最常用也是最简单的实现,第一种将是跨平台通用的,而第二种则在POSIX平台上通用. 跨平台实现 ...

  8. DSAPI多功能组件编程应用-网络相关(上)

    [DSAPI.DLL下载地址]  DSAPI多功能组件编程应用-网络相关,网络相关编程有很多很多,这里讲解一下封装在DSAPI中的网络相关的功能,这些都是本人简化到极点的功能了,可以在软件开发过程中节 ...

  9. DSAPI多功能组件编程应用-文件类

    [DSAPI.DLL下载地址]     本节内容,是属于"无需过多说明"就能使用的功能.由于实在是太简单,我就简单地示例一下. DSAPI.文件类.获取可读性强的字节大小信息(&q ...

  10. JAVA Swing 改变标题栏左上角默认咖啡图标

    前言 最近使用Java的swing开发了一个小程序,想要实现改变标题栏左上角的图标,找了网上的资料,经过了一个下午的尝试,都是未能成功,最后,终于是在Java的一本书上找到了结果 我只能说,网上的东西 ...