P2764 最小路径覆盖问题 网络流输出方案
Posted 昵称很长很长真是太好了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2764 最小路径覆盖问题 网络流输出方案相关的知识,希望对你有一定的参考价值。
题意:
求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(意味着这个点是某条简单路径的起点),那么就顺着这个点输出这个简单路径即可。
代码:
#include<bits/stdc++.h>
using 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<int> 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<m;i++){
int x,y;
cin>>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<<" ";
u=lst[u];
}
cout<<u<<endl;
}
}
cout<<ans<<endl;
}
以上是关于P2764 最小路径覆盖问题 网络流输出方案的主要内容,如果未能解决你的问题,请参考以下文章