题意:有 n 座城市和 m 条有向边,现在要把这 n 座城市分成一些洲,要求: 如果城市 u,v 之间可以互达则两座城市要分在一个洲里面,
每个洲里面的任意两个城市 u,v之间至少需要存在一条边,问做少能够分成多少个洲。
分析:先求强连通分量并进行缩点,重新建图,如果两个强连通分量之间如果存在一条边连接其中一个强连通分量内部的一个点和另一个强
连通分量内部的一个点则连一条边从第一个强连通分量到第二个强连通分量,求出最小路径覆盖即为答案。
#include#include #define clr(x)memset(x,0,sizeof(x))#define min(a,b)(a)<(b)?(a):(b)#define maxn 5005#define maxm 3000000struct node{ int to,next;}e[maxm],ed[maxm];int tot;int tt;int xh[maxn];int head[maxn];void add(int s,int t){ e[tot].to=t; e[tot].next=head[s]; head[s]=tot++;}void add2(int s,int t){ ed[tt].to=t; ed[tt].next=xh[s]; xh[s]=tt++;}int sn,top,ti;int dfn[maxn];int low[maxn];int ins[maxn];int sta[maxn];int col[maxn];void tarjan(int u){ dfn[u]=low[u]=++ti; sta[++top]=u; ins[u]=1; int i,k; for(i=head[u];i;i=e[i].next) { k=e[i].to; if(dfn[k]==0) { tarjan(k); low[u]=min(low[u],low[k]); } else if(ins[k]) low[u]=min(low[u],dfn[k]); } if(dfn[u]==low[u]) { sn++; do { k=sta[top--]; ins[k]=0; col[k]=sn; }while(k!=u); }}int link[maxn];int v[maxn];int find(int x){ int i,k; for(i=xh[x];i;i=ed[i].next) { k=ed[i].to; if(!v[k]) { v[k]=1; if(link[k]==0||find(link[k])) { link[k]=x; return 1; } } } return 0;}int main(){ int t,n,m,i,j; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); clr(head); tot=1; int a,b; while(m--) { scanf("%d%d",&a,&b); add(a,b); } sn=top=ti=0; clr(dfn); clr(col); for(i=1;i<=n;i++) if(!dfn[i]) tarjan(i); clr(xh); tt=1; clr(link); clr(v); for(i=1;i<=n;i++) for(j=head[i];j;j=e[j].next) if(col[i]!=col[e[j].to]) add2(col[i],col[e[j].to]); int sum=0; for(i=1;i<=sn;i++) { clr(v); if(find(i)) sum++; } printf("%d\n",sn-sum); } return 0;}