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]==0continue;
        
if(G3[root][i]==truecontinue;
        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==999999return 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]==0continue;
                
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]==0continue;
            
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;
}

posted on 2007-08-10 12:20  woodfish  阅读(1053)  评论(0编辑  收藏  举报

导航