题目描述

给定有向图 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. python转义字符——重点解释:\b,\n和\r区别

    放在最前面: 有时我们并不想让转义字符生效,我们只想显示字符串原来的意思,这就要用r和R来定义原始字符串.如:print r'\t\r' 实际输出为“\t\r”. 主要参考:AllenW的博客 转义字 ...

  2. React Native (二) ios打包到真机

    每当在模拟器上完成了开发,都想到真机上试试,正好前段时候淘了一个imac. 这里就以打包rndemo到iphone为例,讲一下react ntive ios打包到真机的流程. 一.前置 1.有个iph ...

  3. 痞子衡嵌入式:一表全搜罗常见短距离无线通信协议(Wi-Fi/Bluetooth/ZigBee/Thread...)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是常见短距离无线通信协议. 短距离无线通信是物联网的基础,随着物联网IoT的火热发展,各种短距离无线通信协议也是层出不穷,这些协议标准各有 ...

  4. tuxedo开发

    近来一直在和某电信的系统做对接开发,需要从对方系统(tuxedo)中查询数据后进行显示,本来是个挺简单的事情,无奈tuxedo这个东西以前真是没听说过,网上能用的资料也不多,真是苦了我这段时间,还好已 ...

  5. python之错误调试

    无论谁写的程序,必定会存在bug,解决bug需要我们去调试程序.于是乎,在Python中,就会好几种调试手段,如print.assert.logging.pdb.pdb.set_trace() 一.使 ...

  6. SQL Server分页存储过程通用存储过程

    CREATE proc [dbo].[p_paging]@tableName varchar(8000),          --表名.视图名@indexCol varchar(50) = 'id', ...

  7. MySQL 笔记整理(6) --全局锁和表锁:给表加个字段怎么有这么多阻碍

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 6) --全局锁和表锁:给表加个字段怎么有这么多阻碍 数据库锁设计的初衷是处理并发问题.作为多用户共享的资源,当出现并发访问的时候, ...

  8. 微信小程序支付接入注意点

    一.微信支付后台服务器部署 服务器采用ubuntu16.04 + php7.0 + apache2.0. 微信支付后台服务使用了curl 和 samplexml ,因此php.ini配置中必须开启这两 ...

  9. [Go] Go的WaitGroup计数信号量

    WaitGroup是一个计数信号量,可以用来记录并维护运行的goroutine,如果WaitGroup的值大于0,Wait方法就会阻塞 调用Done方法来减少WaitGroup的值,并最终释放main ...

  10. 解决java web中safari浏览器下载后文件中文乱码问题

    解决java web中safari浏览器下载后文件中文乱码问题 String fileName = "测试文件.doc"; String userAgent = request.g ...