pku 1639 野餐计划(最小限度生成树)

很久以前就打算写一下的,无奈关于最小限度生成树的论文是在看不下去,觉得实现起来很复杂,昨天晚上在hyh的要求下坚持看完了论文并实现了出来。

题目大意是:
矮人虽小却喜欢乘坐巨大的轿车,轿车大到可以装下无论多少矮人。某天,N(N≤5000)个矮人打算到野外聚餐。为了集中到聚餐地点,矮人A要么开车到矮人B家中,留下自己的轿车在矮人B家,然后乘坐B的轿车同行;要么直接开车到聚餐地点,并将车停放在聚餐地。
虽然矮人的家很大,可以停放无数量轿车,但是聚餐地点却最多只能停放K辆轿车。现在给你一张加权无向图,它描述了N个矮人的家和聚餐地点,要你求出所有矮人开车的最短总路程。

    [输入文件]
第一行是整数M(M<=49000),接下来M行描述了M条道路。每行形式如同:S1 S2 x,S1和S2均是大于0小于5000的整数,x小于等于1000。最后一行包含两个整数k,root。root表示聚餐的地点。

    [输出文件]
仅一行,形式如同:Total miles driven: xxxXxx是整数,表示最短总路程。

    [输入输出实例]

 Picnic.in    Picnic.out   
5 1 2 1 2 3 1 3 4 1 4 5 1 5 6 1 1 1    Total miles driven: 5    

my code:
#include  < cstdio >
#include 
< iostream >
#include 
< string >
#include 
< map >
using   namespace  std;

const   int  maxn = 50 ;
int  G[maxn][maxn],G2[maxn][maxn];         // G原图 G2去掉限度节点后的图
int  label[maxn],tot[maxn];                 // label对G2进行编号,统一连同分量的节点标号相同;tot[i]表示标号为i的节点个数
bool  u[maxn],G3[maxn][maxn],b[maxn],u2[maxn];     // u:dfs编号时的标记;G3:最小生成树 
int  k,root,m,minid,maxid,id,maxdel,me1,me2;         // id表示当前强连同分量的标号值
map < string , int >  namedic;

inline 
int  min( int  a, int  b) {  return  a < b ? a:b; }
inline 
int  max( int  a, int  b) {  return  a > b ? a:b; }

// 读入数据
void  input() {
    
int  x;
    
string  s1,s2;
    namedic[
" Park " ] = 1 ;
    minid
= 1 ;root = 1 ;
    maxid
= 1 ;
    memset(G,
0 , sizeof  G);
    scanf(
" %d " , & m);
    
for ( int  i = 0 ;i < m;i ++ ) {
        cin
>> s1 >> s2 >> x;
        
if (namedic[s1] == 0 ) namedic[s1] = ( ++ maxid);
        
if (namedic[s2] == 0 ) namedic[s2] = ( ++ maxid);
        G[namedic[s1]][namedic[s2]]
= G[namedic[s2]][namedic[s1]] = x;
    }
    scanf(
" %d " , & k);
}

// dfs求强连同分量并标号
void  dfs( int  v) {
    u[v]
= true ;
    tot[id]
++ ;
    label[v]
= id;
    
for ( int  i = minid;i <= maxid;i ++
        
if (G2[v][i] > 0 &&! u[i]) {
            dfs(i);
        }
}


// 计算从root->v出发的圈中除root<->v外的边的最大值maxdel
// me1,me2记录最大边的端点
bool  circlemax( int  v, int  j) {
    u2[v]
= true ;
    
for ( int  i = minid;i <= maxid;i ++ ) {
        
if ( ! G3[v][i])  continue ;
        
if (u2[i])  continue ;
        
if (i == root) {
            maxdel
= j;
            
return   true ;
        }
        
if (j < G[v][i]) {
            me1
= v;me2 = i;
        }
        
if (circlemax(i,max(j,G[v][i])))  return   true ;
    }
    
return   false ;
}

// 对k限度生成树进行一次换边操作
int  addmstedge() {
    
int  bestdel =- 1 ,bestret = 999999 ,me1b,me2b;
    
int  addv;
    
for ( int  i = minid;i <= maxid;i ++ ) {
        
if (G[root][i] == 0 continue ;
        
if (G3[root][i] == true continue ;
        memset(u2,
false , sizeof  u2);
        circlemax(i,
0 );
        
// printf("%d\n",maxdel);
         if (G[root][i] - maxdel < bestret) {
            bestret
= G[root][i] - maxdel;
            addv
= i;
            me1b
= me1,me2b = me2;
        }
    }
    
// printf("%d\n",bestret);
    G3[root][addv] = G3[addv][root] = true ;
    G3[me1b][me2b]
= G3[me2b][me1b] = false ;
    
// printf("%d %d %d\n",addv,me1b,me2b);
    
    
if (bestret == 999999 return   0 ; else   return  bestret;
}

// 求连同分量并标号
void  make_cc() {
    
for ( int  i = minid;i <= maxid;i ++ ) {
        
for ( int  j = minid;j <= maxid;j ++ ) {
            G2[i][j]
= G[i][j];
            
if (i == root || j == root) G2[i][j] = 0 ;
        }
    }
    memset(u,
false , sizeof  u);
    memset(tot,
0 , sizeof  tot);
    id
= 0 ;u[root] = true ;
    
for ( int  i = minid;i <= maxid;i ++ ) {
        
if (i != root)
            
if ( ! u[i]) {
                id
++ ;
                dfs(i);
            }
    }
}

// 计算标号为id的连同分量的最小生成树
int  calc_mst( int  id) {
    
int  sp,ep,sum = 0 ;
    
for ( int  i = minid;i <= maxid;i ++ ) {
        
if (label[i] == id) {
            b[i]
= true ;
            
break ;
        }
    }
    
for ( int  i = 1 ;i < tot[id];i ++ ) {
        
int  min = 999999 ;
        
for ( int  j = minid;j <= maxid;j ++ ) {
            
if (label[j] != id)  continue ;
            
if ( ! b[j])  continue ;
            
for ( int  kk = minid;kk <= maxid;kk ++ ) {
                
if (label[kk] != id)  continue ;
                
if (b[kk])  continue ;
                
if (G2[j][kk] == 0 continue ;
                
if (G2[j][kk] < min) {
                    min
= G2[j][kk];
                    sp
= j;
                    ep
= kk;
                }
            }
        }
        sum
+= min;
        b[ep]
= true ;
        G3[sp][ep]
= G3[ep][sp] = true ;
    }
    
return  sum;    
}

void  solve() {
    
int  sum = 0 ;
    make_cc();
    memset(b,
false , sizeof  b);
    memset(G3,
false , sizeof  G3);
    
for ( int  i = 1 ;i <= id;i ++ )
        sum
+= calc_mst(i);
    
for ( int  i = 1 ;i <= id;i ++ ) {
        
int  min = 999999 ;
        
int  nowid;
        
for ( int  j = minid;j <= maxid;j ++ ) {
            
if (label[j] != i)  continue ;
            
if (G[root][j] == 0 continue ;
            
if (G[root][j] < min) {
                min
= G[root][j];
                nowid
= j;
            }
        }
        sum
+= min;
        G3[root][nowid]
= G3[nowid][root] = true ;
    }
    
// for(int i=minid;i<=maxid;i++)
    
//     for(int j=i;j<=maxid;j++)
    
//         if(G3[i][j]) printf("%d %d\n",i,j);
    
// printf("%d\n",sum);
     int  bestans = sum,ret = bestans;
    
for ( int  nowx = id;nowx < k;nowx ++ ) {
        bestans
= bestans + addmstedge();
        ret
= min(bestans,ret);
    }
    printf(
" Total miles driven: %d\n " ,ret);
}

int  main() {
    input();
    solve();
    
return   0 ;
}

你可能感兴趣的:(pku)