最大团(有向图的强连通分量+缩点+DAG上的dp)

【问题描述】
 
  给你一张有向图 G,求一个结点数最大的结点集,使得该结点集中的任意两个结点 u 和 v 满足:要么 u 可以达 v,要么 v 可以达 u(u,v相互可达也行)。
 
【输入格式】
 
  第一行为结点数 n 和边数 m ,结点编号 1~n。
  以下m行每行两个整数 u 和 v ,表示一条有向边 u->v。。
 
【输出格式】
 
  输出最大结点集的结点数。
 
【输入样例】
 
5 5
1 2
2 3
3 1
4 1
5 2
 
【输出样例】
 
4
 
【数据范围】
 

0<=n<=10000,0<=m<=50000


分析:

1.有向图,自然是求强联通分量;

2.求出分量后缩点,每个点的值就是这个分量包含的节点数,于是做DAG上的dp即可;


代码:

#include
#include
using namespace std;
const int maxn=10005;
const int maxm=50005;
int n,m,np=0,np2=0,last2[maxn],last[maxn],f[maxn],vis[maxn];
struct edge{int to,pre;}E[maxm],E2[maxm];

char  c;
void qkscanf(int &x)
{
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}

void addedge(int u,int v)
{
	E[++np]=(edge){v,last[u]};
	last[u]=np;
}

void addedge2(int u,int  v)
{
	E2[++np2]=(edge){v,last2[u]};
	last2[u]=np2;
}

int dfn[maxn],low[maxn],stk[maxn],belong[maxn],size[maxn],top=0,dfs_clock=0,scc_cnt=0;
void DFS(int i)
{
	dfn[i]=low[i]=++dfs_clock;
	stk[++top]=i;
	for(int p=last[i];p;p=E[p].pre)
	{
		int j=E[p].to;
		if(dfn[j])
		{
			if(!belong[j]) low[i]=min(low[i],dfn[j]);
			continue;
		}
		
		DFS(j);
		low[i]=min(low[i],low[j]);
	}
	
	if(low[i]==dfn[i])
	{
		scc_cnt++;
		while(1)
		{
			int x=stk[top--];
			belong[x]=scc_cnt;
			size[scc_cnt]++;
			if(x==i) break;
		}
	}
}

void suodian()
{
	for(int i=1;i<=n;i++)
	{
		for(int p=last[i];p;p=E[p].pre)
		{
			int j=E[p].to;
			if(belong[i]!=belong[j]) addedge2(belong[i],belong[j]);
		}
	}
}

int d[maxn];
int DFS2(int i)
{
	if(vis[i]) return d[i];
	int t=size[i];
	for(int p=last2[i];p;p=E2[p].pre)
	{
		int j=E2[p].to;
		t=max(t,DFS2(j)+1);
	}
	vis[i]=1;
	return d[i]=t;
}

int main()
{
//	freopen("in.txt","r",stdin);
	qkscanf(n);qkscanf(m);
	int u,v;
	for(int i=1;i<=m;i++)
	{
		qkscanf(u);qkscanf(v);
		addedge(u,v);
	}
	
	for(int i=1;i<=n;i++) if(!dfn[i]) DFS(i);//求出分量 
	
	suodian();//缩点 
	
	int ans=0;//DAG上的dp 
	for(int i=1;i<=scc_cnt;i++) ans=max(ans,DFS2(i));
	
	printf("%d",ans);
	
	return 0;
} 

你可能感兴趣的:(图论-强连通分量)