最小树形图poj3164

 

         之前学习过这个算法,后来没有复习,昨天上海网络赛的C题就出现了,而在OJ上的例子也不多。当时一味的同Kruska去解,头都大了,完全忘记了有向图应该用最小树形图。昨晚回头来看,还看了一段时间才明白,真是,只怪自己做题的态度吧,这下边是一个别人写得代码,也不记得再哪儿转的了,没出处,不好意思,只想用来阐述这个算法。

#include <stdio.h>
#include <string.h>
#include <math.h>
double gh[101][101], mincost[101],ans;
int prev[101],stack[101],id[101],mark[101],numv,nume,cnt,top,sta,
    x[101],y[101];
int scaned[101];
void Combine() {
    int i,j,now,x;
    double t;
    now=stack[sta];//now为当前缩点的编号,stack记录了环上的点
    for (i=sta;i<top;i++)
{
        x=stack[i];//x是环中的点
        ans+=mincost[x];//首先得加上环上所有最小入度,后边再加上那个最小差值就好
        id[x]=now;//改变环上所有点的id
        for (j=1;j<=numv;j++)
   {
            if (gh[j][x]!=-1)
    {
                t=gh[j][x]-mincost[x];//所有其他边到当前集合的边权-最小的权==当前缩点的入度
                if (t<gh[j][now] || gh[j][now]==-1) gh[j][now]=t;
            }
            if ((gh[x][j]!=-1 && gh[x][j]<gh[now][j]) || gh[now][j]==-1) gh[now][j]=gh[x][j];//更新处理出度
        }
    }
    for (i=2;i<=numv;i++) //id[i]==i是那些没在环上的点
   if (id[i]==i) prev[i]=id[prev[i]];//更新处理路径
mincost[now]=1e20;
    for (i=1;i<=numv;i++)
   if (i!=now && id[i]==i && gh[i][now]!=-1 && gh[i][now]<mincost[now]) //求出更新后的最小入度,记录路径
    mincost[now]=gh[i][now],prev[now]=i;
}
int Find_Circle() {
    int i,temp,mark[101];
    memset(scaned,0,sizeof(scaned));
    for (i=numv;i>1;i--)
{
        if (id[i]!=i || scaned[i]) continue;
        memset(mark,0,sizeof(mark));
        top=1;
        temp=i;
        while (temp!=1 && !mark[temp] && !scaned[temp])
   {
            scaned[temp]=1;
            mark[temp]=top;
            stack[top++]=temp;
            temp=prev[temp];
        }
        if (mark[temp]) //如果在这些边集中找到坏
   {
            sta=mark[temp];
            return 1;
        }
    }
    return 0;
}
void DFS(int v) {
int i;
    cnt++;
    scaned[v]=1;
    for (i=1;i<=numv;i++) if (!scaned[i] && gh[v][i]!=-1) DFS(i);
}
void Minimum_Arborescence () {
int i;
    while (Find_Circle()) Combine();
    for (i=2;i<=numv;i++)
   if (i==id[i]) ans+=mincost[i];
}
int main () {
    int i,j,v1,v2,val;
    while (scanf("%d%d",&numv,&nume)!=EOF) {
        for (i=1;i<=numv;i++) for (j=1;j<=numv;j++) gh[i][j]=-1;
        memset(scaned,0,sizeof(scaned));

        for (i=1;i<=numv;i++) scanf("%d%d",&x[i],&y[i]);
        for (i=0;i<nume;i++) {
            scanf("%d%d",&v1,&v2);
            gh[v1][v2]=sqrt((x[v1]-x[v2])*(x[v1]-x[v2])+(y[v1]-y[v2])*(y[v1]-y[v2]));
        }

        cnt=0;
        DFS(1);
        if (cnt<numv) printf("poor snoopy\n"); //不连通
        else {
            id[1]=1;
            for (i=2;i<=numv;i++) {
                id[i]=i;
                mincost[i]=1e20;
                for (j=1;j<=numv;j++)
      if(i!=j && gh[j][i]!=-1 && gh[j][i]<mincost[i])        //找到所有点的各自最小入边,记录路径
       mincost[i]=gh[j][i],prev[i]=j;
            }
            ans=0;
            Minimum_Arborescence();
            printf("%.2lf\n",ans);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值