题意:
给你N个人,M条年龄大小的关系,现在打算把这些人分成不同的集合,使得每个集合的任意两个人之间的年龄是不可比的。问你最小的集合数是多少?
分析:
首先,假设有一个环,那么这个环中的任意两个点之间都是可比的,并且,和这个环相连的任意一个点或环也和这个环是可比的,因为关系具有传递性。但如果两个点或者环,无法处在同一条路径上,那么这两个点和环就是不可比的。所以,如果我们把这些环--强连通分量缩为一个点。强连通分量的点数就是缩点后的点权。那么缩点后的新图就是一个有向带权无环图,题目就是要求我们求出这个有向带权无环图的关键路径----最长路径。(因为那些较短的路径上的点总可以和较长的路径点和为一点)。
//First Edit Time: 2014-11-25 13:38
//Last Edit Time: 2014-11-25 14:24
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std; #define _clr(x, y) memset(x, y, sizeof (x))
#define Min(x, y) (x < y ? x : y)
#define Max(x, y) (x > y ? x : y)
#define INF 0x3f3f3f3f
#define N 100010 vector<int> map[N], new_map[N];
int low[N], dfn[N];
int Stack[N], be[N];
bool instack[N];
int dist[N], sum[N];
int top, n, cnt, time; void Init()
{
top = time = cnt = ;
_clr(instack, );
_clr(Stack, );
_clr(dist, -);
_clr(low, );
_clr(sum, );
_clr(dfn, );
_clr(be, );
for(int i=; i<=n; i++)
{
map[i].clear();
new_map[i].clear();
}
} //dfs生成树求强连通分量
void dfs(int u)
{
dfn[u] = low[u] = ++time;
Stack[top++] = u;
instack[u] = true;
for(int i=; i<(int)map[u].size(); i++)
{
int v = map[u][i];
if(!dfn[v])
{
dfs(v);
low[u] = Min(low[u], low[v]);
}
else if(instack[v])
low[u] = Min(low[u], dfn[v]);
}
if(dfn[u]==low[u])
{
cnt++;
do
{
u = Stack[--top];
instack[u] = false;
be[u] = cnt;
sum[cnt]++;
}while(dfn[u] != low[u]);
}
} void Tarjan_Scc()
{
for(int i=; i<=n; i++)
{
if(!dfn[i])
dfs(i);
}
for(int i=; i<=n; i++)
{
for(int j=; j<(int)map[i].size(); j++)
{
int v = map[i][j];
if(be[i] != be[v])
new_map[be[i]].push_back(be[v]);
}
}
} int Get(int u)
{
if(dist[u]!=-) return dist[u];
dist[u] = ;
for(int i=; i<(int)new_map[u].size(); i++)
dist[u] = Max(Get(new_map[u][i]), dist[u]);
return dist[u] = dist[u] + sum[u];
} int main()
{
int m, x, y;
while(~scanf("%d%d",&n,&m))
{
Init();
for(int i=; i<m; i++)
{
scanf("%d%d",&x, &y);
map[x].push_back(y);
} Tarjan_Scc(); //Tarjan算法求强连通缩点为一个DAG int ans = -INF;
for(int i=; i<=cnt; i++) //记忆化搜索求DAG关键路径
ans = Max(Get(i), ans); printf("%d\n", ans);
}
return ;
}