题解 P2764 【最小路径覆盖问题】
I_AM_HelloWord · · 题解
讲一下思路吧,套路的拆点,将每个点拆成两个,一个入点,一个出点,
如果原图中两个点之间有一条有向边,那么就将拆点后的图该点的出点连一条容量为1的边到另一点的入点,
然后建立一个超源,超汇,把超源向每个出点连边,入点向超汇连边,然后跑最大流。
每条路径的开头就是入点中没有匹配的点,跑最大流的时候记录一下每个入点对应的出点就行了。
大家都吐槽此题没写special judge,所以用vector邻接表输出方案会不对,
所以蒟蒻为自己曾是一名pas党感到庆幸,因为pascal的邻接表写法就是大家说的链式前向星,
时空常数比较小^_^,写习惯了,就不咋用vector了。
二分图定理之一:最小路径覆盖数=顶点数-最大匹配,这个应该都知道吧?
参考代码(提交时务必手动调语言为C++,而C++11编译过不去):
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=2000;
const int M=60010;
struct Edge{
int to,cap,next;
}e[M*2];
int d[N],a[N],cur[N],num[N],fa[N],next[N];
bool vis[N];
queue<int> Q;
int n,m,S,T,EdgeCnt=0,tn=0;
int read(){
int x=0,f=1;char ch=getchar();
while (ch<'0' || ch>'9'){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
void addedge(int u,int v,int w){
int &p=EdgeCnt;
e[p].to=v;e[p].cap=w;e[p].next=a[u];
a[u]=p++;
}
void BFS(){
for (int i=1;i<=n;i++)d[i]=n;
Q.push(T);d[T]=0;
while (!Q.empty()){
int u=Q.front();Q.pop();
for (int p=a[u];p!=-1;p=e[p].next){
int v=e[p].to;
if (e[p^1].cap && d[v]>d[u]+1){
d[v]=d[u]+1;
Q.push(v);
}
}
}
}
int Augment(){
int u=T,f=INF;
while (u!=S){
next[fa[u]]=u;
u=fa[u];
f=min(f,e[cur[u]].cap);
}
u=T;
while (u!=S){
u=fa[u];
e[cur[u]].cap-=f;
e[cur[u]^1].cap+=f;
}
return f;
}
int MaxFlow(){
n=2*n+2;
memset(num,0,sizeof(num));
BFS();
for (int i=1;i<=n;i++)num[d[i]]++,cur[i]=a[i];
int u=S,flow=0;
while (d[S]<n){
if (u==T){
flow+=Augment();u=S;
}
bool done=false;
for (int p=cur[u];p!=-1;p=e[p].next){
int v=e[p].to;
if (e[p].cap && d[u]==d[v]+1){
done=true;fa[v]=u;cur[u]=p;u=v;
break;
}
}
if (!done){
int m=n-1;
for (int p=a[u];p!=-1;p=e[p].next){
int v=e[p].to;
if (e[p].cap)m=min(m,d[v]);
}
if (--num[d[u]]==0)break;
num[d[u]=m+1]++;
cur[u]=a[u];
if (u!=S)u=fa[u];
}
}
return flow;
}
int main(){
memset(a,0xff,sizeof(a));
n=read(),m=read();tn=n;
for (int i=1;i<=m;i++){
int u,v;
u=read(),v=read();
addedge(u,v+n,1);addedge(v+n,u,0);
}
S=2*n+1,T=2*n+2;
for (int i=1;i<=n;i++)
addedge(S,i,1),addedge(i,S,0),
addedge(i+n,T,1),addedge(T,i+n,0);
int ans=tn-MaxFlow();
for (int i=1;i<=tn;i++)
if (!vis[i]){
for (int j=i;j;j=next[j]){
if (j>tn)j-=tn;
printf("%d ",j,next[j]);
vis[j]=1;
}
printf("\n");
}
printf("%d",ans);
return 0;
}