POJ - 3164 Command Network 最小树形图--有向图的最小生成树

题意:

给出可以连接的两个点,并且是单向边,问最后最小生成树。

分析:

朱刘算法模板:

#include <cstdio>
#include <cmath>
#include <cstring>

using namespace std;

const int maxn=105;

#define INF 0x3f3f3f3f

double p[maxn][2];//坐标
int pre[maxn];//最短弧前驱
int vis[maxn];//点标记
int used[maxn];//查环过程的标记
double Map[maxn][maxn];//存图关系
int n,m,u,v,len;

double length(double x1,double y1,double x2,double y2){
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

double zhuliu(int root)
{
    double sum=0;
    int i,j,k;
    memset(vis,0,sizeof vis);
    while(1){
        for(i=1;i<=n;i++){  //1、求最短弧集合pre
            if(vis[i]||i==root) continue;
            pre[i]=i;
            for(int j=1;j<=n;j++)   //找i点的最短前驱弧
                if(!vis[j]&&Map[j][i]<Map[pre[i]][i])
                    pre[i]=j;
            if(pre[i]==i) return -1;    //弱图不连通
        }
        for(i=1;i<=n;i++){  //2、查环
            if(vis[i]||i==root) continue;
            memset(used,0,sizeof used);
            used[root]=1;
            k=i;
            while(!used[k]){
                used[k]=1;
                k=pre[k];
            }
            if(k!=root) break;//存在环
        }
        if(i>n){     //不存在环了
            for(j=1;j<=n;j++)
                if(j!=root&&!vis[j])
                    sum+=Map[pre[j]][j];
            return sum;
        }
        i=k;  //3、下面将这个环缩到i点;
        do{   //4、先累加环记录下环权值
            sum+=Map[pre[k]][k];
            k=pre[k];
        }while(k!=i);
        do{//5、修改环上点的前驱边,为准备环收缩
            for(j=1;j<=n;j++)
                if(!vis[j]&&Map[j][k]<INF&&j!=pre[k])
                    Map[j][k]-=Map[pre[k]][k];
            k=pre[k];
        }while(k!=i);
        for(j=1;j<=n;j++){       //6、环收缩到i点
            if(j==i||vis[j])continue;
            for(k=pre[i];k!=i;k=pre[k]){        //k点的对外距离给i点
                if(Map[i][j]>Map[k][j])Map[i][j]=Map[k][j];
                if(Map[j][i]>Map[j][k])Map[j][i]=Map[j][k];
            }
        }
        for(k=pre[i];k!=i;k=pre[k])vis[k]=1;//7、将环上除i外全标记
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<104;i++)
            for(int j=0;j<104;j++)
                Map[i][j]=INF*1.0;
        for(int i=1;i<=n;i++)
            scanf("%lf%lf",&p[i][0],&p[i][1]);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            if(u!=v) Map[u][v]=length(p[u][0],p[u][1],p[v][0],p[v][1]);
        }
        double ans=zhuliu(1);
        if(ans<0) puts("poor snoopy");
        else printf("%.2f\n",ans);
    }
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值