传送门
解析:
其实最小路径覆盖没有什么特别的,还是一个匹配问题,我们将这个问题转化成网络流。
发现我们其实初始可以将每个点都放上一个伞兵,这样伞兵个数显然是 n n n,然后我们发现每一个点的伞兵可以由经过他前一个点的伞兵代替,于是我们将这条路径标记一下。
发现每个点最多在所有入边里面产生一个匹配,在所有出边里面产生一个匹配。
我们将每一个点拆成两个,按照边的出入关系建图,跑二分图最大匹配就行了。
代码:
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
inline void outint(int a){
static char ch[13];
if(a==0)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
cs int N=250,M=20004,INF=0x3f3f3f3f;
cs int S=0,T=249;
int last[N],nxt[M<<1],to[M<<1],ecnt;
int cap[M<<1];
inline void addedge(int u,int v,int val){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,cap[ecnt]=val;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,cap[ecnt]=0;
}
int lev[N],cur[N];
inline bool BFS(){
memset(lev,-1,sizeof lev);
queue<int> q;
q.push(S),lev[S]=0,cur[S]=last[S];
while(!q.empty()){
int u=q.front();
q.pop();
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]==-1){
lev[v]=lev[u]+1;
if(v==T)return true;
cur[v]=last[v];
q.push(v);
}
}
}
return false;
}
inline int Dinic(cs int &u,cs int &flow){
if(u==T)return flow;
int ans=0;
for(int &e=cur[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]>lev[u]){
int delta=Dinic(v,min(flow-ans,cap[e]));
if(delta){
cap[e]-=delta;
cap[e^1]+=delta;
ans+=delta;
if(ans==flow)return flow;
}
}
}
return ans;
}
inline int maxflow(){
int ans=0;
while(BFS())ans+=Dinic(S,INF);
return ans;
}
inline void init(){
ecnt=1;
memset(last,0,sizeof last);
}
int n,m,t;
signed main(){
t=getint();
while(t--){
init();
n=getint();
m=getint();
for(int re i=1;i<=m;++i){
int u=getint(),v=getint();
addedge(u,v+120,1);
}
for(int re i=1;i<=n;++i)addedge(S,i,1),addedge(i+120,T,1);
outint(n-maxflow());pc('\n');
}
return 0;
}