【HDU】1827 Summer Holiday 强连通缩点

Summer Holiday

Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1489    Accepted Submission(s): 676


Problem Description
To see a World in a Grain of Sand
And a Heaven in a Wild Flower,
Hold Infinity in the palm of your hand
And Eternity in an hour.
                  —— William Blake

听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?
 

Input
多组测试数组,以EOF结束。
第一行两个整数N和M(1<=N<=1000, 1<=M<=2000),表示人数和联系对数。
接下一行有N个整数,表示Wiskey联系第i个人的电话费用。
接着有M行,每行有两个整数X,Y,表示X能联系到Y,但是不表示Y也能联系X。
 

Output
输出最小联系人数和最小花费。
每个CASE输出答案一行。
 

Sample Input
   
   
   
   
12 16 2 2 2 2 2 2 2 2 2 2 2 2 1 3 3 2 2 1 3 4 2 4 3 5 5 4 4 6 6 4 7 4 7 12 7 8 8 7 8 9 10 9 11 10
 

Sample Output
   
   
   
   
3 6
 

Author
威士忌
 

Source
HDOJ 2007 Summer Exercise(3)- Hold by Wiskey

传送门:【HDU】1827 Summer Holiday

题目分析:
先缩点,缩点的同时得到每个分量里的最小值,最后统计入度为0的分量,将他们的最小值加起来就是答案。

代码如下:

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

#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define CLEAR( a , x ) memset ( a , x , sizeof a )

const int MAXN = 1005 ;
const int MAXE = 2005 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , n ;
	Edge ( int v = 0 , int n = 0 ) : v ( v ) , n ( n ) {}
} ;

struct SCC {
	Edge edge[MAXE] ;
	int adj[MAXN] , cntE ;
	int Dfn[MAXN] , Low[MAXN] ;
	int dfs_clock , scc_cnt ;
	int S[MAXN] , top ;
	int scc[MAXN] ;
	bool ins[MAXN] ;
	int mmin[MAXN] ;
	int cost[MAXN] ;
	
	void init () {
		cntE = 0 ;
		scc_cnt = 0 ;
		dfs_clock = 0 ;
		CLEAR ( Dfn , 0 ) ;
		CLEAR ( ins , 0 ) ;
		CLEAR ( adj , -1 ) ;
		CLEAR ( mmin , INF ) ;
	}
	
	void addedge ( int u , int v ) {
		edge[cntE] = Edge ( v , adj[u] ) ;
		adj[u] = cntE ++ ;
	}
	
	void Tarjan ( int u ) {
		Dfn[u] = Low[u] = ++ dfs_clock ;
		ins[u] = true ;
		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 ( Dfn[u] == Low[u] ) {
			++ scc_cnt ;
			while ( 1 ) {
				int v = S[-- top] ;
				scc[v] = scc_cnt ;
				mmin[scc_cnt] = min ( mmin[scc_cnt] , cost[v] ) ;
				ins[v] = 0 ;
				if ( v == u )
					break ;
			}
		}
	}
	
	void find_SCC ( int n ) {
		REPF ( i , 1 , n )
			if( !Dfn[i] )
				Tarjan ( i ) ;
	}
} ;

SCC C ;
bool in[MAXN] ;

void work () {
	int n , m ;
	int u , v ;
	while ( ~scanf ( "%d%d" , &n , &m ) ) {
		C.init () ;
		REPF ( i , 1 , n )
			scanf ( "%d" , &C.cost[i] ) ;
		while ( m -- ) {
			scanf ( "%d%d" , &u , &v ) ;
			C.addedge ( u , v ) ;
		}
		C.find_SCC ( n ) ;
		if ( C.scc_cnt == 1 ) {
			printf ( "1 %d\n" , C.mmin[1] ) ;
			continue ;
		}
		CLEAR ( in , false ) ;
		REPF ( u , 1 , n )
			for ( int i = C.adj[u] ; ~i ; i = C.edge[i].n ) {
				int v = C.edge[i].v ;
				if ( C.scc[u] != C.scc[v] )
					in[C.scc[v]] = true ;
			}
		int ans = 0 , num = 0 ;
		REPF ( i , 1 , C.scc_cnt ) {
			if ( !in[i] )
				++ num , ans += C.mmin[i] ;
		}
		printf ( "%d %d\n" , num , ans ) ;
	}
}
	

int main () {
	work () ;
	return 0 ;
}


你可能感兴趣的:(图论,HDU)