题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2299
题目大意:给定一个图,求最大的一个点集合,这个点集合的性质:任意选择两个点u,v,满足从u到v有路径或者从v到u有路径。
思路:一个强联通分量内的所有的点肯定是满足性质的,先考虑用tarjan缩点,现在整个图就变成了DAG,每个点赋予一个权值,这个权值就是该强联通分量原来所含有的一个点值。然后就是求权值最大的一条路径,考虑是DAG,可以使用dp求解,中间有一个小优化,就是一个点的dp值已经更新过的话,就没必要再以这个点为起点再跑一遍了,因为得到的答案肯定不会更优。(也可以理解为只从入度为0的点开始跑)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
vector<int> e[maxn],e1[maxn];
stack<int> s;
int cnt,num;
int dfn[maxn],low[maxn],bl[maxn],tot[maxn],dp[maxn];
void tarjan(int u){
dfn[u]=++cnt;
s.push(u);
low[u]=dfn[u];
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(bl[v]==0){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
++num;
while(!s.empty()){
int k=s.top();
bl[k]=num;
s.pop();
if(u==k) break;
}
}
}
void init(int n){
cnt=num=0;
while(!s.empty()) s.pop();
for(int i=1;i<=n;i++){
e[i].clear();
dfn[i]=low[i]=bl[i]=tot[i]=0;
e1[i].clear();
dp[i]=0;
}
}
void dfs(int u){
for(int i=0;i<e1[u].size();i++){
int v=e1[u][i];
dp[v]=max(dp[u]+tot[v],dp[v]);
dfs(v);
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++){
tot[bl[i]]++;
for(int j=0;j<e[i].size();j++){
int v=e[i][j];
if(bl[i]!=bl[v]){
e1[bl[i]].push_back(bl[v]);
}
}
}
int ans=0;
for(int i=1;i<=num;i++){
if(!dp[i]){
dp[i]=tot[i];
dfs(i);
}
}
for(int i=1;i<=num;i++) ans=max(ans,dp[i]);
printf("%d\n",ans);
}
return 0;
}