hdu 4463 Outlets(最小生成树)

题意:n个点修路,要求总长度最小,但是有两个点p、q必须相连

思路:完全图,prim算法的效率取决于节点数,适用于稠密图。用prim求解。

p、q间距离设为0即可,最后输出时加上p、q间的距离

prim算法:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;

#define INF 15000//计算得最长值
#define MAXN 128
bool vis[MAXN];
double lowc[MAXN];

struct Point{
    double x,y;
}p[MAXN];

double dis(Point a,Point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double prim(double cost[][MAXN],int n){//标号从0开始
    double ans=0,minc;
    int i,j,p;
    memset(vis,false,sizeof(vis));
    vis[0]=true;
    for(i=1;i<n;++i)lowc[i]=cost[0][i];
    for(i=1;i<n;++i){
        minc=INF;
        p=-1;
        for(j=0;j<n;++j)
            if(!vis[j]&&lowc[j]<minc){
                minc=lowc[j];
                p=j;
            }
        if(minc==INF)return -1;//原图不连通
        ans+=minc;
        vis[p]=true;
        for(j=0;j<n;++j)
            if(!vis[j]&&cost[p][j]<lowc[j])
                lowc[j]=cost[p][j];
    }
    return ans;
}

int main(){
    int n,m,a,b,i,j;
    double cost[MAXN][MAXN],w;
    while(~scanf("%d",&n)&&n){
        //m=n*(n-1)/2;//m边条数
        scanf("%d%d",&a,&b);
        --a;--b;
        for(i=0;i<n;++i)
            scanf("%lf%lf",&p[i].x,&p[i].y);

        for(i=0;i<n;++i)
            for(j=i+1;j<n;++j){
                w=dis(p[i],p[j]);
                if((i==a&&j==b)||(j==a&&i==b))w=0;
                cost[i][j]=cost[j][i]=w;
            }
        printf("%.2f
",prim(cost,n)+dis(p[a],p[b]));
    }
    return 0;
}
View Code

kruskal算法的效率取决于边数,适用于稀疏图。

边数为50*50,也不是很多,也可用kruskal算法:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;

#define MAXN 110//最大点数
#define MAXM 10000//最大边数
int F[MAXN];//并查集使用

struct Point{
    double x,y;
}p[MAXN];

struct Edge{
    int u,v;
    double w;
}edge[MAXM];//存储边的信息,包括起点/终点/权值
int tol;//边数,加边前赋值为0

double dis(Point a,Point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

void addedge(int u,int v,double w){
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol++].w=w;
}

//排序函数,将边按照权值从小到大排序
bool cmp(Edge a,Edge b){
    return a.w<b.w;
}

int find(int x){
    if(F[x]==-1)return x;
    return F[x]=find(F[x]);
}

//传入点数,返回最小生成树的权值,如果不连通返回-1
double kruskal(int n){
    memset(F,-1,sizeof(F));
    sort(edge,edge+tol,cmp);
    int cnt=0;//计算加入的边数
    int i,u,v,t1,t2;
    double w,ans=0;
    for(i=0;i<tol;++i){
        u=edge[i].u;
        v=edge[i].v;
        w=edge[i].w;
        t1=find(u);
        t2=find(v);
        if(t1!=t2){
            ans+=w;
            F[t1]=t2;
            ++cnt;
        }
        if(cnt==n-1)break;
    }
    if(cnt<n-1)return -1;//不连通
    return ans;
}

int main(){
    int n,a,b,i,j;
    double w;
    while(~scanf("%d",&n)&&n){
        scanf("%d%d",&a,&b);
        for(i=1;i<=n;++i)
            scanf("%lf%lf",&p[i].x,&p[i].y);

        tol=0;
        for(i=1;i<=n;++i)
            for(j=i+1;j<=n;++j){
                w=dis(p[i],p[j]);
                if((i==a&&j==b)||(j==a&&i==b))w=0;
                addedge(i,j,w);
            }
        printf("%.2f
",kruskal(n)+dis(p[a],p[b]));
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/gongpixin/p/4782294.html