hdu 4126

/*假设两个各自连通的部分分别为树A,树B
1. 用dp[i][j]表示树A中的点i 到 树B(j点所在的树)的最近距离,
这个过程可以在一边dfs就可以出来,对于每个 i 的dfs 复杂度是O(n) ,
外加一个n的循环求出每个点,这里的总复杂度为 O(n^2)。
2. 通过求出来的dp[i][j] 再用一个dfs 求出  树B 到 树A的最近距离,
(方法:枚举树A中的所有点 到 树B的最近距离,取其中的最小值。)
显然, 这个求出来的值是我们要的最小替代边,把它保存到一个best[i][j]数组里面,
(best[i][j]表示去掉边<i,j>后它的最小替代边的值)
这里的总复杂度为 O(n^2)。*/
#include<string.h>
#include<stdio.h>
#include<math.h>
#define N 3005

#define M 9000000
typedef long long LL;

struct edge{
    int to,next,w;
}e[N<<1];

int map[N][N],dp[N][N],best[N][N],d[N],head[N],pre[N];
int n,m,ans,mm,inf,o;
bool v[N];
LL anss;
void add(int x,int y)
{   e[o].to=y;
    e[o].w=map[x][y];
    e[o].next=head[x];
    head[x]=o++;
}
bool prim(int s)
{   int i,j,k,ma;
    for (i=0;i<n;i++)
        {d[i]=map[s][i];
         pre[i]=s;
        }
    memset(v,0,sizeof(v));
    v[s]=1;
    for (i=1;i<n;i++)
        {ma=inf;
         for (j=0;j<n;j++)
            if (!v[j]&&d[j]<ma) {ma=d[j]; k=j;}

         v[k]=1; ans+=d[k];
         add(pre[k],k);
         add(k,pre[k]);
         for (j=0;j<n;j++)
            if (!v[j]&&map[k][j]<d[j])
                {d[j]=map[k][j];
                 pre[j]=k;
                }
        }
    return 1;
}
inline int min(int a,int b)
{   return a>b?b:a;
}
void dfs1(int r,int now,int t)     //dp[r][now]表示r到 now为根的子树的 最小距离
{   int j;
    v[now]=1;
    if (t>1)dp[r][now]=map[r][now];  //很关键!
    for (int k=head[now];k!=-1;k=e[k].next)
         if (!v[e[k].to])
         {   j=e[k].to;
             v[j]=1;
             dfs1(r,j,t+1);
             dp[r][now]=min(dp[r][now],dp[r][j]);
         }
}
void dfs2(int r,int now,int t)    //best[r][now]表示r为根的子树 到 now为根的子树的 最小距离
{   int j;
    v[r]=1;
    best[r][now]=dp[r][now];//很关键!
    for (int k=head[r];k!=-1;k=e[k].next)
        if (!v[e[k].to])
         {   j=e[k].to;
             dfs2(j,now,t+1);
             best[r][now]=min(best[r][now],best[j][now]);
         }

}
void doit()
{   int i,j,x,y,z;
    anss=0; ans=0;
    memset(map,0x3F,sizeof(map));
    inf=map[0][0];
    memset(dp,0x3F,sizeof(dp));
    memset(best,0x3F,sizeof(best));
    o=0; memset(head,255,sizeof(head));
    for (i=0;i<n;i++)
         map[i][i]=0;
    for (i=1;i<=m;i++)
        {scanf("%d%d%d",&x,&y,&z);
         if (x==y) continue;
         map[x][y]=z;
         map[y][x]=z;
        }
    prim(0);
    for (i=0;i<n;i++)
         {   memset(v,0,sizeof(v));
             dfs1(i,i,0);
         }
    for (i=0;i<n;i++)
         {   memset(v,0,sizeof(v));
             dfs2(i,i,0);
         }
    scanf("%d",&mm);
    for (i=1;i<=mm;i++)
        {scanf("%d%d%d",&x,&y,&z);
         if (pre[x]!=y&&pre[y]!=x)
         anss+=ans;//很巧妙
         else anss=anss+ans-map[x][y]+min(best[x][y],z);

        }
    printf("%.4lf\n",double(anss)/mm);
}

int main()
{
    while (scanf("%d%d",&n,&m)&&n) doit();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值