pku 3164 最小树形图

这题本质是求最小树形图。
在所有操作开始之前,我们需要把图中所有的自环全都清除。很明显,自环是不可能在任何一个树形图上的。只有进行了这步操作,总算法复杂度才真正能保证是O(VE)。
首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。现在所有的最小入边都选择出来了,如果这个入边集不存在有向环的话,我们可以证明这个集合就是该图的最小树形图。这个证明并不是很难。如果存在有向环的话,我们就要将这个有向环所称一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。为什么入边的权要减去in[u],这个后面会解释,在这里先给出算法的步骤。然后可以证明,新图中最小树形图的权加上旧图中被收缩的那个环的权和,就是原图中最小树形图的权。
  上面结论也不做证明了。现在依据上面的结论,说明一下为什么出边的权不变,入边的权要减去in [u]。对于新图中的最小树形图T,设指向人工节点的边为e。将人工节点展开以后,e指向了一个环。假设原先e是指向u的,这个时候我们将环上指向u的边 in[u]删除,这样就得到了原图中的一个树形图。我们会发现,如果新图中e的权w'(e)是原图中e的权w(e)减去in[u]权的话,那么在我们删除掉in[u],并且将e恢复为原图状态的时候,这个树形图的权仍然是新图树形图的权加环的权,而这个权值正是最小树形图的权值。所以在展开节点之后,我们得到的仍然是最小树形图。逐步展开所有的人工节点,就会得到初始图的最小树形图了。

code:

// 最小树形图,源点编号为1
#include  < cstdio >
#include 
< cstring >
#include 
< cmath >
using   namespace  std;
 
struct  Point {
    
double  _x,_y;
    Point(
double  x, double  y) :_x(x),_y(y) {}
    Point() {}
};

const  unsigned  int  maxn = 128 ;
const   double  NOEDGE = 99999999 ;
double  G[maxn][maxn];
Point allp[maxn];
int  N,M;
double  res; 

inline 
double  dist( const  Point &  p1, const  Point &  p2) {
    
return  sqrt((p1._x - p2._x) * (p1._x - p2._x) + (p1._y - p2._y) * (p1._y - p2._y));
}
template 
< class  T >
void  update(T &  o, const  T &  x){
    
if (o > x)
        o
= x;
}
bool  vis[maxn]; 
void  dfs( int  v){
    vis[v]
= true ;
    
for ( int  i = 2 ;i <= N; ++ i)
        
if (( ! vis[i]) && G[v][i] != NOEDGE)
            dfs(i); 
}
bool  possible(){
    memset(vis,
0 , sizeof (vis));
    dfs(
1 );
    
for ( int  i = 2 ;i <= N; ++ i)
        
if ( ! vis[i])
            
return   false ;
    
return   true ;
}
int  pre[maxn];
bool  del[maxn];
void  solve(){
    
int  num = N;
    memset(del,
0 , sizeof (del));
    
for (;;){
        
int  i;
        
for (i = 2 ;i <= N; ++ i){
            
if (del[i]) continue ;
            pre[i]
= i;
            G[i][i]
= NOEDGE;
            
for ( int  j = 1 ;j <= N; ++ j){
                
if (del[j]) continue ;
                
if (G[j][i] < G[pre[i]][i])
                    pre[i]
= j;
            }
        }
        
for (i = 2 ;i <= N; ++ i){
            
if (del[i]) continue ;
            
int  j = i;
            memset(vis,
0 , sizeof (vis));
            
while ( ! vis[j] && j != 1 ){
                vis[j]
= true ;
                j
= pre[j];
            }
            
if (j == 1 ) continue ;
            i
= j;
            res
+= G[pre[i]][i];
            
for (j = pre[i];j != i;j = pre[j]){
                res
+= G[pre[j]][j];
                del[j]
= true ;
            }
            
for (j = 1 ;j <= N; ++ j){
                
if (del[j]) continue ;
                
if (G[j][i] != NOEDGE)
                    G[j][i]
-= G[pre[i]][i];
            }
            
for (j = pre[i];j != i;j = pre[j]){
                
for ( int  k = 1 ;k <= N; ++ k){
                    
if (del[k]) continue ;
                    update(G[i][k],G[j][k]);
                    
if (G[k][j] != NOEDGE)
                        update(G[k][i],G[k][j]
- G[pre[j]][j]);
                }
            }
            
for (j = pre[i];j != i;j = pre[j]){
                del[j]
= true ;
            }
            
break ;
        }
        
if (i > N){
            
for ( int  i = 2 ;i <= N; ++ i){
                
if (del[i]) continue ;
                res
+= G[pre[i]][i];
            }
            
break ;
        }
    }
}
int  main(){
    
double  x,y;
    
for (;;){
        
if (scanf( " %d%d " , & N, & M) == EOF)  return   0 ;
        
if (N == 0 ) break ;
        
for ( int  i = 0 ;i <= N;i ++ )
            
for ( int  j = 0 ;j <= N;j ++ )
                G[i][j]
= NOEDGE;
        
for ( int  i = 1 ;i <= N;i ++ ) {
            scanf(
" %lf %lf " , & x, & y);
            allp[i]._x
= x;
            allp[i]._y
= y;
        }
        
for ( int  i = 0 ;i < M; ++ i){
            unsigned 
int  a,b;
            scanf(
" %u%u " , & a, & b);
            update(G[a][b],dist(allp[a],allp[b]));
        }
        
if ( ! possible()){
            puts(
" poor snoopy " ); 
        }
        
else {
            res
= 0
            solve();
            printf(
" %.2f\n " ,res);
        }
    }
}


你可能感兴趣的:(pku)