题解——Watering Hole(最小生成树+虚拟节点)

题解——Watering Hole(最小生成树+虚拟节点)

今天考试T1,这么水,我居然写了一个更加复杂的生成树


题面魔改版

Description
学园都市迎来了经济萧条期,火力发电厂接连倒闭.瞅准了商机,商人BerryKanry向操控电磁的能力者——御坂美琴发出了合作邀请,准备开一家电力公司.
他包揽了学园都市N个区域的供电项目.一个区域如果想得到电力,要么在这个区域建立一个超级电源,要么和已经获得电力的某城市建立电力纽带.他的合作意图是让御坂美琴提供电力,而他负责策划具体电路设计.御坂美琴开出的合作条件是这样的:
对于区域i(1 <= i <= N),在此区域创造一个超级电源的费用是wi,在两个区域i和j之间建立电力纽带的费用是pij.
BerryKanry想知道,如何设计一种方案使N个区域都得到供电的同时,自己向御坂美琴支付的合作费用最少呢?
Input
第一行:一个数n
第二行到第n+1行:第i+1行含有一个数wi
第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表pij。
Output
第一行:一个单独的数代表最小合作费用.
Sample Input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Sample Output
9
Sample Explanation
BerryKanry在区域4上建立超级电源,然后其他的区域都与区域4建立电力纽带,这样就要花费3+2+2+2=9.

思路

由于带了点权,导致点权和边权的矛盾,并没有办法跑出一个合法的最小生成树。

那么

把点权下放给虚拟的边做边权不就可以了吗?

建立一个虚拟汇点,和所有电源连边,然后,没有然后,保证连通就可以了,跑一个最小生成树。(正确性显然)

标准code:

#include<bits/stdc++.h>
using namespace std ; 
const  int  MAXN = 305 ;
inline int read(){
	int s= 0 ; char g = getchar() ;  while( g>'9'||g<'0')g=getchar() ; 
	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ;
	return s ;
} 
struct ap{
	int  to , from , val ;
}t[ MAXN*MAXN ] , p[ MAXN+10 ];
int  w[ MAXN ] ,  a[ MAXN ][ MAXN ] , fa[ MAXN ] , wk[ MAXN ] ;
int  N , M , tot = 0 , ans = 0 , cnt = 0 ;
int  to[ MAXN*2 ] , nex[ MAXN*2 ] , head[ MAXN ] , toot = 0 ; 
bool cmp( ap x , ap y ){
	return x.val < y.val ; 
}
void  add( int  x , int  y ){
	to[ ++toot ] = y , nex[ toot ] = head[ x ] , head[ x ] = toot ;
}
int  find( int x ){
	if( x != fa[ x ] ) return fa[ x ] = find( fa[ x ] ) ;
	else return x ;
}
int main(){
	//freopen("Berry.in","r",stdin);
	//freopen("Berry.out","w",stdout);
	N = read() ; int  tol ;
	for( int i = 1 ; i <= N ; ++i ){
		w[ i ] = read() , fa[ i ] = i ;
		if( ans > w[ i ] )ans = w[ i ] , tol = i ;
	}
	for( int i = 1 ; i <= N ; ++i )
	    for( int j = 1 ; j <= N ; ++j )
	    	t[ ++tot ].from = i , t[ tot ].to = j , t[ tot ].val = read() ;
	for( int i = 1 ; i <= N ; ++i )
	    t[ ++tot ].from = N+1 , t[ tot ].to = i , t[ tot ].val = w[ i ] ;//虚拟节点
	sort( t+1 , t+tot+1 , cmp ) ;
	for( int i = 1 ; i <= tot ; ++i ){
		int x = find( t[i].from ) , y = find( t[i].to ) ;
		if( x == y )continue ;
		fa[ x ] = y ;
		ans += t[i].val ; 
		cnt++ ;
		if( cnt >= N )break ;
	}
	cout<<ans ; 
}

关于我的玄学做法

我是在原图上建立最小生成树,然后有一个奇妙的性质,每次新建一个电源,都可以使最小生成树上至少减少一条边。(正确性已证)

对于每两个电源,他们最短路径上就可以删掉一条边。

对于初始的最小生成树,我们直接加入w[ ]最小的电源,然后按边权从大到小判断是加电源即可。这个部分分很妙。

#include<bits/stdc++.h>
using namespace std ; 
const  int  MAXN = 305 ;
inline int read(){
	int s= 0 ; char g = getchar() ;  while( g>'9'||g<'0')g=getchar() ; 
	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ;
	return s ;
} 
struct ap{
	int  to , from , val ;
}t[ MAXN*MAXN ] , p[ MAXN+10 ];//但正确性,证明了90%plus,要是WA了,就怪那些剩下的。
int  w[ MAXN ] ,  a[ MAXN ][ MAXN ] , fa[ MAXN ] , wk[ MAXN ] ;// 时间是卡着上线的,随时可能TLE 
int  N , M , tot = 0 , cnt = 0 , ans = (1<<30) , wknum = 0 , dfsans , dfstol ;
int  to[ MAXN*2 ] , nex[ MAXN*2 ] , head[ MAXN ] , toot = 0 , dfsfrom , dfsto ; 
bool  used[ MAXN ] ; //生成树中的边 
bool cmp( ap x , ap y ){
	return x.val < y.val ; 
}
bool cmp2( ap x , ap y ){
	return x.val > y.val ;
}
void  add( int  x , int  y ){
	to[ ++toot ] = y , nex[ toot ] = head[ x ] , head[ x ] = toot ;
}
int  find( int x ){
	if( x != fa[ x ] ) return fa[ x ] = find( fa[ x ] ) ;
	else return x ;
}
void  dfs( int  u , int fa , bool che ){
	if( che ){
		if( dfsans > w[ u ] )dfsans = w[ u ] , dfstol = u ;
	}
	for( int i = head[ u ] ; i ; i = nex[ i ] ){
		if( to[ i ] == fa )continue ;
		if( che || ( dfsto == u && dfsfrom == to[ i ] ) || ( dfsfrom == u && dfsto == to[ i ]  ) )
		    dfs( to[ i ] , u , 1 ) ;
		else dfs( to[ i ] , u , 0 ) ;
	}
}
int main(){
	freopen("Berry.in","r",stdin);
	freopen("Berry.out","w",stdout);
	N = read() ; int  tol ;
	for( int i = 1 ; i <= N ; ++i ){
		w[ i ] = read() , fa[ i ] = i ;
		if( ans > w[ i ] )ans = w[ i ] , tol = i ;
	}
	wk[ ++wknum ] = tol ;//首站,相同情况下正确性可证明 
	for( int i = 1 ; i <= N ; ++i )
	    for( int j = 1 ; j <= N ; ++j )
	    	t[ ++tot ].from = i , t[ tot ].to = j , t[ tot ].val = read() ;
	sort( t+1 , t+tot+1 , cmp ) ;
	for( int i = 1 ; i <= tot ; ++i ){
		int x = find( t[i].from ) , y = find( t[i].to ) ;
		if( x == y )continue ;
		fa[ x ] = y ;
		ans += t[i].val ; 
		p[ ++cnt ].from = t[i].from , p[cnt].to=t[i].to , p[cnt].val = t[i].val ;
		add( t[i].from , t[i].to ) , add( t[i].to , t[i].from ) ;
		if( cnt >= N-1 )break ;
	}//克鲁斯卡尔 
	sort( p+1 , p+cnt+1 , cmp2 ) ;
	for( int i = 1 ; i <= cnt ; ++i ){
		dfsfrom = p[i].from , dfsto = p[i].to ;dfsans = (1<<30) , dfstol = 0 ;
		for( int k = 1 ; k <= wknum ; ++k )// dfsans , dfstol , dfsfrom , dfsto
			 dfs( k , k , 0 ) ;
		if( dfsans <= p[i].val ){//相同情况下优先建立 
			ans = ans + dfsans - p[i].val ; //建立
			wk[ ++wknum ] = dfstol , w[ dfstol ] = 0 ;//防止重复建厂 
		}
	}
	//for( int i = 1 ; i <= cnt ; ++i )cout<<p[ i ].from <<" "<< p[i].to <<" "<<p[i].val<<endl  ;
	cout<<ans ; 
}

如有不足,请大佬指出

posted @ 2019-07-27 14:06  蓝银杏-SSW  阅读(408)  评论(0编辑  收藏  举报
//结束