ZOJ3795 Grouping(强连通分量+缩点+记忆化搜索)
题目给一张有向图,要把点分组,问最少要几个组使得同组内的任意两点不连通。
首先考虑找出强连通分量缩点后形成DAG,强连通分量内的点肯定各自一组,两个强连通分量的拓扑序能确定的也得各自一组。
能在同一组的就是两个强连通分量在不同的从入度0到出度0的强连通分量的路径上。
那么算法很直观就能想到了,用记忆化搜索,d[u]表示从强连通分量u出发到出度为0的强连通分量最少要几个组(最多有几个点)。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 110000 6 #define MAXM 330000 7 struct Edge{ 8 int u,v,next; 9 }edge[MAXM]; 10 int head[MAXN],NE; 11 void addEdge(int u,int v){ 12 edge[NE].u=u; edge[NE].v=v; edge[NE].next=head[u]; 13 head[u]=NE++; 14 } 15 int bn,belong[MAXN],size[MAXN]; 16 int dn,dfn[MAXN],low[MAXN]; 17 int top,stack[MAXN]; bool instack[MAXN]; 18 void tarjan(int u){ 19 dfn[u]=low[u]=++dn; 20 stack[++top]=u; instack[u]=1; 21 for(int i=head[u]; i!=-1; i=edge[i].next){ 22 int v=edge[i].v; 23 if(dfn[v]==0){ 24 tarjan(v); 25 low[u]=min(low[u],low[v]); 26 }else if(instack[v]){ 27 low[u]=min(low[u],dfn[v]); 28 } 29 } 30 if(dfn[u]==low[u]){ 31 int v; ++bn; 32 do{ 33 v=stack[top--]; 34 instack[v]=0; 35 belong[v]=bn; ++size[bn]; 36 }while(u!=v); 37 } 38 } 39 int d[MAXN]; 40 int dfs(int u){ 41 if(d[u]) return d[u]; 42 int res=0; 43 for(int i=head[u]; i!=-1; i=edge[i].next){ 44 int v=edge[i].v; 45 res=max(res,dfs(v)); 46 } 47 return d[u]=res+size[u]; 48 } 49 int main(){ 50 int n,m,a,b; 51 while(~scanf("%d%d",&n,&m)){ 52 NE=0; 53 memset(head,-1,sizeof(head)); 54 while(m--){ 55 scanf("%d%d",&a,&b); 56 addEdge(a,b); 57 } 58 top=dn=bn=0; 59 memset(dfn,0,sizeof(dfn)); 60 memset(instack,0,sizeof(instack)); 61 memset(size,0,sizeof(size)); 62 for(int i=1; i<=n; ++i){ 63 if(dfn[i]==0) tarjan(i); 64 } 65 66 int tmp=NE; NE=0; 67 memset(head,-1,sizeof(head)); 68 for(int i=0; i<tmp; ++i){ 69 int u=belong[edge[i].u],v=belong[edge[i].v]; 70 if(u==v) continue; 71 addEdge(u,v); 72 } 73 memset(d,0,sizeof(d)); 74 int res=0; 75 for(int i=1; i<=bn; ++i){ 76 res=max(res,dfs(i)); 77 } 78 printf("%d\n",res); 79 } 80 return 0; 81 }