poj 3723 Conscription(Kruskal + 并查集)
题意:windy要组建一支军队,召集了N个女孩和M个男孩,每个人要付10000RMB,但是如果一个女孩和一个男孩有关系d的,且已经付给了其中一个人的钱,那么就可以付给另一个人10000-d元,求windy最少要付多少钱。
解题过程:写了一个prim的最小生成树才发现原来整个图不是连通的,所以想到用Kruskal,刚开始的时候val中存的是10000-d,然后求最小生成树,但是最后求有几棵树的时候不是很好计算,改了一下f[x]、f[y]的赋值规则还是不行,然后看到有人说求最大生成树,然后就改了一下条件就AC了。
代码:
View Code
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> #include <math.h> #define N 50005 #define M 20005 using namespace std ; struct node { int s , e ; int val ; }p[N] ; int f[M] , n , m , r , num ; int cmp ( const node a , const node b ) { return a.val > b.val ; } void init() { int i ; for ( i = 0 ; i <= n + m ; i++ ) f[i] = i ; } void add ( int x , int y , int z ) { p[num].s = x ; p[num].e = y ; p[num].val = z ; num++ ; } int find ( int x ) { if ( f[x] != x ) f[x] = find ( f[x] ) ; return f[x] ; } int main() { int cas , i , x , y , z ; //freopen( "input.txt" , "r" , stdin) ; scanf ( "%d" , &cas ) ; while ( cas-- ) { scanf ( "%d%d%d" , &n , &m , &r ) ; num = 0 ; for ( i = 1 ; i <= r ; i++ ) { scanf ( "%d%d%d" , &x , &y , &z ) ; add ( x + 1 , y + n + 1 , z ); } sort ( p , p + num , cmp ) ; init() ; int sum = ( n + m ) * 10000 ; for ( i = 0 ; i < num ; i++ ) { x = find ( p[i].s ) ; y = find ( p[i].e ) ; if ( x != y ) { sum -= p[i].val ; f[x] = y ; } } for ( i = 1 ; i <= n + m ; i++ ) cout<<f[i]<<" "; cout<<endl ; /*sort ( f , f + n + m + 1 ) ; x = 1 ; for ( i = 2 ; i <= n + m ; i++ ) if ( f[i] != f[i-1] ) x++ ;*/ printf ( "%d\n" , sum ) ; } return 0 ; }