POJ - 3164 Command Network(最小树形图)

Command Network
题意:在有向图中找出最小生成树(最小树形图)

/*
Command Network 
POJ - 3164
https://vjudge.net/problem/POJ-3164
题意:在有向图中找出最小生成树(最小树形图)
解法:朱,刘算法
算法思路:
1:确定一个根
2:找到除根外每一个点的最小入边,若这些边构成了环,则缩环成点,并将环内的每一个点的其他入边都减去环内的入边
3:重复步骤2直到没有环出现(构成了树)。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 10000
#define INF 0x3f3f3f3f
int X[maxn],Y[maxn];
double IN[maxn];
int PRE[maxn];
int ID[maxn];
int VIS[maxn];
int n,m,cnt;
struct node{
  int u,v;
  double w;
}edge[maxn];
double dtc(int a,int b){
  return sqrt((double)((X[a]-X[b])*(X[a]-X[b])+(Y[a]-Y[b])*(Y[a]-Y[b])));
}
void add(int u,int v){
  edge[cnt].u=u;
  edge[cnt].v=v;
  edge[cnt++].w=dtc(u,v);
}
double zhuliu(int root){
  double ans=0;
  while(1){
    for(int i=1;i<=n;i++){
      IN[i]=INF;
    }
    for(int i=0;i<cnt;i++){
      if(edge[i].v==edge[i].u) continue;
      else if(IN[edge[i].v]>edge[i].w){
        PRE[edge[i].v]=edge[i].u;//记录每个点的最小入边
        IN[edge[i].v]=edge[i].w;//记录每个点的最小入边的边权
      }
    }
    for(int i=1;i<=n;i++){
      if(i!=root&&IN[i]==INF) return -1;
    }
    int num=0;
    memset(ID,0,sizeof ID);
    memset(VIS,0,sizeof VIS);
    //ID数组的初始化要与cnt记录的环数相匹配, 这里ID用0初始化, 则cnt从1开始记录, 也可以用-1初始化, 让cnt从0开始记录
    //初始化要注意, 因为这里的节点是从1开始编号的, 所以VIS数组中不会出现0, 可以用0来初始化, 若节点是从0开始编号, 则VIS中会有0, 不能用0初始化
    IN[root]=0;
    for(int i=1;i<=n;i++){
      ans+=IN[i];
      int v=i;
      while(!ID[v]&&v!=root&&VIS[v]!=i){
        VIS[v]=i;
        v=PRE[v];
      }
      if(v!=root&&!ID[v]){
        ID[v]=++num;
        for(int j=PRE[v];j!=v;j=PRE[j]){
          ID[j]=num;
        }
      }
    }
    if(!num) break;//没有环, 树成立
    for(int i=1;i<=n;i++){
      if(!ID[i]){
        ID[i]=++num;
      }
    }
    for(int i=0;i<m;i++){
      int x=edge[i].u,y=edge[i].v;
      edge[i].u=ID[x];
      edge[i].v=ID[y];
      if(ID[x]!=ID[y]){
        edge[i].w-=IN[y];
      }
    }
    n=num;
    root=ID[root];//维护新的点数目和根
  }
  return ans;
}
int main(){
  int u,v;
  while(~scanf("%d %d", &n, &m)){
    cnt=0;
    for(int i=1;i<=n;i++){
      scanf("%d%d", &X[i], &Y[i]);
    }
    for(int i=0;i<m;i++){
      scanf("%d%d", &u, &v);
      if(u!=v) add(u,v);
    }
    double ans=zhuliu(1);
    if (ans == -1) printf("poor snoopy\n");
    else printf("%.2f\n", ans);
  }
  return 0;
}

参考博客:
算法学习——最小树形图

发布了70 篇原创文章 · 获赞 22 · 访问量 6498

猜你喜欢

转载自blog.csdn.net/weixin_44410512/article/details/102069721