【UVa】11324 The Largest Clique 强连通缩点+DP

Problem B: The Largest Clique

Given a directed graph G, consider the following transformation. First, create a new graphT(G) to have the same vertex set as G. Create a directed edge betweentwo vertices u and v in T(G) if and only if there is a pathbetween u and v in G that follows the directed edges only in the forwarddirection. This graph T(G) is often called the transitive closure of G.

We define a clique in a directed graph as a set of vertices U such thatfor any two vertices u and v in U, there is a directededge either from u to v or from v to u (or both).The size of a clique is the number of vertices in the clique.

The number of cases is given on the first line of input. Each test case describes a graph G.It begins with a line of two integersn and m, where 0 ≤ n ≤ 1000 is the number ofvertices of Gand 0 ≤ m ≤ 50,000 is the number of directed edges of G.The vertices of G are numbered from 1 to n.The following m lines contain two distinct integers u and vbetween 1 and n which definea directed edge from u to v in G.

For each test case, output a single integer that is the size of the largest clique in T(G).

Sample input

1
5 5
1 2
2 3
3 1
4 1
5 2

Output for sample input

4

Zachary Friggstad


题目大意:

给一张n个结点、m条边的有向图G,求一个结点数最大的结点集,使得该结点集中任意两个结点u和v满足:要么u可以到达v,要么v可以到达u(u和v相互可达也行)。(0 <= n <= 1000,0 <= m <= 50000)结点编号1~n。


题目分析:

很明显的强连通缩点+DAG最长路。不难发现在最优方案中,同一个强连通分量中的点要么都选,要么不选。把强连通分量缩点后得到SCC图,让SCC图的结点的权值等于它代表的强连通分量的结点数,则题目转化成为求SCC图上点权最大的路径。由于SCC是一个DAG图,所以用DAG上的动态规划求解即可。


代码如下:


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;

#define clear( A , X ) memset ( A , X , sizeof A )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FF( i , a , b ) for ( int i = a ; i <= b ; ++ i )

const int maxN = 1005 ;
const int maxS = 20005 ;
const int maxQ = 20005 ;
const int maxE = 1000005 ;
const int oo = 0x3f3f3f3f ;
struct Edge {
	int v , n ;
	Edge () {}
	Edge ( int var , int next ) : v(var) , n(next) {}
} ;

struct DAG_DP {
	Edge edge[maxE] ;
	int adj[maxN] , cntE ;
	int Q[maxQ] , head , tail ;
	int in[maxN] ;
	int d[maxN] ;
	int dis[maxN] ;
	
	void Addedge ( int u , int v ) {
		edge[cntE] = Edge ( v , adj[u] ) ;
		adj[u] = cntE ++ ;
	}
	
	void Init () {
		cntE = 0 ;
		head = tail = 0 ;
		clear ( d , 0 ) ;
		clear ( in , 0 ) ;
		clear ( dis , 0 ) ;
		clear ( adj , -1 ) ;
	}
	
	void DEBUG ( int n ) {
		FF ( i , 1 , n )
			printf ( "d[%d] = %d\n" , i , d[i] ) ;
		FF ( i , 1 , n )
			printf ( "in[%d] = %d\n" , i , in[i] ) ;
	}
	
	void DP ( int n ) {
		int ans = 0 ;
		FF ( i , 1 , n ) {
			if ( !in[i] ) {
				Q[tail ++] = i ;
				dis[i] = d[i] ;
				if ( ans < dis[i] )
					ans = dis[i] ;
			}
		}
		//DEBUG ( n ) ;
		while ( head != tail ) {
			int u = Q[head ++] ;
			for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
				int v = edge[i].v ;
				if ( dis[v] < dis[u] + d[v] ) {
					dis[v] = dis[u] + d[v] ;
					if ( ans < dis[v] )
						ans = dis[v] ;
				}
				if ( 0 == ( -- in[v] ) )
					Q[tail ++] = v ;
			}
		}
		printf ( "%d\n" , ans ) ;
	}
} ;

struct SCC {
	Edge edge[maxE] ;
	DAG_DP DAG ;
	int adj[maxN] , cntE ;
	int scc[maxN] , Dfn[maxN] , Low[maxN] ;
	int dfs_clock , scc_cnt ;
	int S[maxS] , ins[maxN] , top ;
	
	void Addedge ( int u , int v ) {
		edge[cntE] = Edge ( v , adj[u] ) ;
		adj[u] = cntE ++ ;
	}
	
	void Init () {
		top = 0 ;
		cntE = 0 ;
		scc_cnt = 0 ;
		dfs_clock = 0 ;
		clear ( Dfn , 0 ) ;
		clear ( ins , 0 ) ;
		clear ( adj , -1 ) ;
	}
	
	void Tarjan ( int u ) {
		Dfn[u] = Low[u] = ++ dfs_clock ;
		ins[u] = 1 ;
		S[top ++] = u ;
		for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
			int v = edge[i].v ;
			if ( !Dfn[v] ) {
				Tarjan ( v ) ;
				Low[u] = min ( Low[u] , Low[v] ) ;
			}
			else if ( ins[v] ) {
				Low[u] = min ( Low[u] , Dfn[v] ) ;
			}
		}
		if ( Low[u] == Dfn[u] ) {
			++ scc_cnt ;
			while ( 1 ) {
				int v = S[-- top] ;
				ins[v] = 0 ;
				scc[v] = scc_cnt ;
				if ( v == u ) break ;
			}
		}
	}
	
	void Find_SCC ( int n ) {
		FF ( i , 1 , n )
			if ( !Dfn[i] )
				Tarjan ( i ) ;
	}
	
	void Graph_Build ( int n ) {
		DAG.Init () ;
		FF ( i , 1 , n )
			++ DAG.d[scc[i]] ;
		FF ( u , 1 , n ) {
			for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
				int v = edge[i].v ;
				if ( scc[u] != scc[v] ) {
					DAG.Addedge ( scc[u] , scc[v] ) ;
					++ DAG.in[scc[v]] ;
				}
			}
		}
	}
} ;

SCC scc ;

void work () {
	int n , m , u , v ;
	scc.Init () ;
	scanf ( "%d%d" , &n , &m ) ;
	REP ( i , m ) {
		scanf ( "%d%d" , &u , &v ) ;
		scc.Addedge ( u , v ) ;
	}
	scc.Find_SCC ( n ) ;
	
	if ( scc.scc_cnt == 1 ) {
		printf ( "%d\n" , n ) ;
		return ;
	}
	scc.Graph_Build ( n ) ;
	scc.DAG.DP ( n ) ;
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- )
		work () ;
	return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值