传送门:【POJ】3723 Conscription
题目分析:由于一个连通块从哪个节点开始选择最后的花费都是一样的,所以可以成功转化成最小生成树问题。其实本题就是最小生成树的变种——最大生成树,倒过来加边然后和减去总花费就好了。然后我竟然特意设了一个编号为0的节点,然后对所有的节点建边10000,然后其他的边变成10000-c,最后跑最小生成树就好了。。。看了别人的代码顿时觉得自己真是奇葩。。。简单的题目都想复杂了。。唉唉唉唉唉唉= =
代码如下:
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define clear( a , x ) memset ( a , x , sizeof a ) typedef long long LL ; const int MAXN = 20005 ; const int MAXE = 70005 ; const int oo = 0x3f3f3f3f ; struct Line { int x , y , c ; Line () {} Line ( int x , int y , int c ) : x ( x ) , y ( y ) , c ( c ) {} void input () { scanf ( "%d%d%d" , &x , &y , &c ) ; } bool operator < ( const Line &a ) const { return c < a.c ; } } ; struct MST { Line line[MAXE] ; int p[MAXN] ; int N , M , NV , NE ; int find ( int x ) { return p[x] == x ? x : ( p[x] = find ( p[x] ) ) ; } void kruskal () { int ans = 0 , cnt = 1 ; sort ( line , line + NE ) ; REPF ( i , 0 , NV ) p[i] = i ; REP ( i , NE ) { int x = find ( line[i].x ) ; int y = find ( line[i].y ) ; if ( x != y ) { ans += line[i].c ; p[x] = y ; if ( NV == ( ++ cnt ) ) break ; } } printf ( "%d\n" , ans ) ; } void input () { scanf ( "%d%d%d" , &N , &M , &NE ) ; NV = N + M + 1 ; REP ( i , NE ) { line[i].input () ; line[i].c = 10000 - line[i].c ; line[i].y += N ; } REPF ( i , 1 , N ) line[NE ++] = Line ( 0 , i , 10000 ) ; REPF ( i , 1 , M ) line[NE ++] = Line ( 0 , i + N , 10000 ) ; } void solve () { input () ; kruskal () ; } } ; MST z ; int main () { int T ; scanf ( "%d" , &T ) ; while ( T -- ) z.solve () ; return 0 ; }