传送门
分析:
本题的结论是:最小路径覆盖=顶点数-原图的最大匹配数
证明(逼逼) :
对于$1$对由$1$条边覆盖的点而言,$点数-1=边数$
倘若对于$n$个由$m$边所覆盖的点而言,则等价于把上述式子归纳,则有:$$n-m=cover$$
而现在我们需要令$cover$最大化,显然我们必定是要取到尽可能多的符合题意的边数。
而在题目中,存在着一个点只能被一条边所覆盖,因此,这代表着,对于一个点,只能够有一条入边,同时也只能有一条出边。
考虑到这个限制,我们考虑将一个点拆成两个点,其中一个点代表入点,另一个点代表出点,根据原图的关系$u o v$,将当前$u$的出点连向下一个点$v$的入点,且边的容量为$1$。
而这样建出来的图,则可以满足一个点只能被一条边入,一条边出的限制。
之后我们发现,我们现在所连出来的的一张图是二分图,故我们可以直接建立超级源点和超级汇点,并在这张图上跑最大流即可以求出最大的边数$m$。因此最终的答案即为 $n-m$。
同时这个题中需要我们求出路径数。而对于这个,我们可以通过这张图的残量网络去求解。我们遍历每一个点,如果发现某一个点的当前容量为$0$,则不断延申这条边直到终点。而又因为在这张图中每条边的容量上限为$1$,故一条全部为$0$的路径必定为答案。而因为这样的过程中,最多遍历每个点$1$次,故时间复杂度为$mathcal{O(n)}$
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
struct Node{
int to,val,next;
}q[maxm<<1];
int head[maxn],cnt=0,dep[maxn],cur[maxn],vis[maxn];
int tag[maxn];
int sp,ep,maxflow;
void init(){
memset(head,-1,sizeof(head));
cnt=2,maxflow=0;
}
void add_edge(int from,int to,int val){
q[cnt].to=to;
q[cnt].val=val;
q[cnt].next=head[from];
head[from]=cnt++;
q[cnt].to=from;
q[cnt].val=0;
q[cnt].next=head[to];
head[to]=cnt++;
}
bool bfs(int n){
for(int i=0;i<=n;i++){
cur[i]=head[i],dep[i]=0x3f3f3f3f;
vis[i]=0;
}
dep[sp]=0;
queue<int>que;
que.push(sp);
while(!que.empty()){
int x=que.front();
que.pop();
vis[x]=0;
for(int i=head[x];i!=-1;i=q[i].next){
int to=q[i].to;
if(dep[to]>dep[x]+1&&q[i].val){
dep[to]=dep[x]+1;
if(!vis[to]){
que.push(to);
vis[to]=1;
}
}
}
}
if(dep[ep]!=inf) return true;
else return false;
}
int dfs(int x,int flow){
int rlow=0;
if(x==ep){
maxflow+=flow;
return flow;
}
int used=0;
for(int i=cur[x];i!=-1;i=q[i].next){
cur[x]=i;
int to=q[i].to;
if(q[i].val&&dep[to]==dep[x]+1){
if(rlow=dfs(to,min(flow-used,q[i].val))){
used+=rlow;
q[i].val-=rlow;
q[i^1].val+=rlow;
if(used==flow) break;
}
}
}
return used;
}
int dinic(int n){
while(bfs(n)){
dfs(sp,inf);
}
return maxflow;
}
void Find_Path(int n){
for(int i=1;i<=n;i++){
if(!tag[i]){
int x=i,flag=0,to=0;
while(x!=ep){
for(int j=head[x];j!=-1;j=q[j].next){
if(q[j].val) continue;
to=q[j].to;
printf("%d ",x);
tag[x]=1,flag=1;
if(to==sp) break;
x=to-n;
break;
}
if(to==sp) break;
}
if(flag) puts("");
}
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
sp=0,ep=2*n+1;
init();
for(int i=0;i<m;i++){
int from,to;
scanf("%d%d",&from,&to);
add_edge(from,to+n,1);
}
for(int i=1;i<=n;i++){
add_edge(sp,i,1);
add_edge(i+n,ep,1);
}
int res=n-dinic(ep);
Find_Path(n);
printf("%d
",res);
return 0;
}