原题:ZOJ 3795 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3795
题目大意:给定一个有向图,要求把点分为k个集合,使得每个集合中的任意两点a, b满足a, b互相不可到达。
分析:求出强连通分量后缩点,得到有向无环图,dfs该图求出各点深度(深度加权,权值为强连通分量大小),深度最大值即答案,
因为这一条路径上任意两点都可从深度小的一点到达深度大的一点,所以任意两点必定属于不同集合,即每个点一个集合;求的最大深度集合后,又可以把其它路径(长度为len)上的各点依次归到集合1..len。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #define Mod 1000000009 using namespace std; #define N 100007 vector<int> G[N],G2[N]; stack<int> stk; int instk[N],cnt,Time,n,m,dep[N]; int low[N],dfn[N],bel[N],num[N]; void tarjan(int u) { low[u] = dfn[u] = ++Time; stk.push(u); instk[u] = 1; for(int i=0;i<G[u].size();i++) { int v = G[u][i]; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(instk[v]) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]) { cnt++; int v; do { v = stk.top(); stk.pop(); instk[v] = 0; bel[v] = cnt; num[cnt]++; }while(u != v); } } void Tarjan() { memset(bel,0,sizeof(bel)); memset(instk,0,sizeof(instk)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(num,0,sizeof(num)); Time = cnt = 0; while(!stk.empty()) stk.pop(); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); } void Build() { int i,j; for(i=1;i<=cnt;i++) G2[i].clear(); for(i=1;i<=n;i++) { for(j=0;j<G[i].size();j++) { int v = G[i][j]; if(bel[i] != bel[v]) G2[bel[i]].push_back(bel[v]); } } } int dfs(int u) { if(dep[u]) return dep[u]; for(int i=0;i<G2[u].size();i++) { int v = G2[u][i]; dep[u] = max(dep[u],dfs(v)); } dep[u] += num[u]; return dep[u]; } int main() { int i,j,u,v; while(scanf("%d%d",&n,&m)!=EOF) { for(i=0;i<=n;i++) G[i].clear(); for(i=0;i<m;i++) { scanf("%d%d",&u,&v); G[u].push_back(v); } Tarjan(); Build(); memset(dep,0,sizeof(dep)); int res = 0; for(i=1;i<=n;i++) res = max(res,dfs(i)); printf("%d\n",res); } return 0; }