分析:
不强制双向到达,求最大团
显然一个强连通分量中的点要么都选,要么都不选
我们可以用Tarjan把强连通分量缩点得到SCC图
SCC图中每一个点的权值就是这个SCC中点的数量
问题就转化成,
在图中选择若干个点使得权值和最大
求SCC图中的最大权路径
由于SCC图是一个DAG,我们可以用dp来解决这个问题
dp转移方程为f[x] = size[x] + max(f[y])
tip
第一次用vector存边,感觉挺好用的
但是由于原图是稠密图,所以还是用邻接表存储较好
TLE的原因是scc和pre数组一定要清零
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int N=1002;
int n,m,scc[N],tot,low[N],pre[N],cnt,size[N];
int f[N],in[N],st[N],tt;
struct node{
int y,nxt;
};
node way[50010];
vector<int> G[N];
stack<int> S;
void add(int u,int w)
{
tt++;
way[tt].y=w;way[tt].nxt=st[u];st[u]=tt;
}
void dfs(int now)
{
pre[now]=low[now]=++tot;
S.push(now); //
for (int i=st[now];i;i=way[i].nxt)
{
int v=way[i].y;
if (!pre[v])
{
dfs(v);
low[now]=min(low[now],low[v]);
}
else if (!scc[v])
{
low[now]=min(low[now],pre[v]);
}
}
if (low[now]==pre[now])
{
cnt++;
int tt=0;
for (;;)
{
int x=S.top();
S.pop();
scc[x]=cnt;
tt++;
if (x==now) break;
}
size[cnt]=tt; //SCC的大小
}
}
void find()
{
memset(pre,0,sizeof(pre));
memset(scc,0,sizeof(scc));
while (!S.empty()) S.pop();
tot=0; cnt=0;
for (int i=1;i<=n;i++)
if (!pre[i])
dfs(i);
}
int dp(int now)
{
if (f[now]) return f[now]; //记忆化
int ans=0;
for (int i=0;i<G[now].size();i++)
{
int v=G[now][i];
ans=max(ans,dp(v));
}
f[now]=ans+size[now];
return f[now];
}
void solve()
{
memset(in,0,sizeof(in));
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++) G[i].clear();
for (int i=1;i<=n;i++)
for (int j=st[i];j;j=way[j].nxt)
{
int v=way[j].y; //i--->v
if (scc[i]!=scc[v]) //不在一个SCC中
{
G[scc[i]].push_back(scc[v]);
in[scc[v]]++;
}
}
int ans=0;
for (int i=1;i<=cnt;i++)
if (!in[i]) //入度为0
ans=max(ans,dp(i));
printf("%d
",ans);
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
memset(st,0,sizeof(st));
tt=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int u,w;
scanf("%d%d",&u,&w);
add(u,w);
}
find();
solve();
}
return 0;
}