HDU 4126 Genghis Khan the Conqueror MST+最佳替代边

题目大意:给你一个无向图,其中有些边有可能增大且每次只增大一条便,每条便增大的可能性相等,问最后产生的最小生成树的期望值为多少。

解题思路:近似与次小生成树的思路,先找一个最小生成树,并找出当生成树中的树边的最佳替代边,

如果增大的边为树边,用增大以后的边和最佳替代边做比较选择性替代,加上新生成树的总边权。

不然就不用做修改,直接加上原树的边权就好。


唯一的难点就是找最佳替代边,之前以为很难,后来发现就那么回事。


把每个点设为树根,然后从下递归回来就好

每递归一次复杂度为(N),总复杂度(N^2)。


代码看一遍就懂了。。。

  

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <stack>
#include <utility>
#include <set>
#include <map>
#include <string>
#include <cmath>
using namespace std;
#define REP(i,n)   for(int i=0;i<(n);++i)
#define FOR(i,l,r) for(int i=(l);i<=(r);++i)
#define DWN(i,r,l) for(int i=(r);i>=(l);--i)
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define N 3100
#define M 6010
#define INF 1e9

vector<int>f[N];
int g[N][N],dp[N][N];
bool visit[N],mst[N][N];
int dis[N],pre[N];
int root;
void init(int n)
{
    FOR(i,0,n) f[i].clear();
    FOR(i,0,n)
        FOR(j,0,n)
            dp[i][j]=g[i][j]=INF;
}
int prime(int n)//一般的prime
{
    memset(visit,0,sizeof(visit));
    memset(mst,0,sizeof(mst));
    REP(i,n) dis[i]=g[0][i],pre[i]=0;
    visit[0]=1;
    dis[n]=INF;
    int sum=0;
    REP(aa,n-1)
    {
        int k=n;
        REP(i,n) if(!visit[i] && dis[i]<dis[k]) k=i;

        visit[k]=1;
        f[k].pb(pre[k]);
        f[ pre[k] ].pb(k);
        sum+=dis[k];
        mst[k][ pre[k] ]=mst[ pre[k] ][k]= 1;

        REP(i,n) if(!visit[i] && g[k][i]<dis[i]) dis[i]=g[k][i],pre[i]=k;
    }
    return sum;
}

int dfs(int cur,int fa)//往下递归找最佳替代边
{   //ret表示能从root覆盖到cur点及cur以下的最小边的权值
    int ret=INF;
    REP(i,(int)f[cur].size())
    {
        if(f[cur][i]!=fa)
            ret=min(ret,dfs(f[cur][i],cur));
    }
    if(cur!=root && !mst[root][cur])
    {
        ret=min(ret,g[root][cur]);
    }
    dp[fa][cur]=dp[cur][fa]=min(dp[cur][fa],ret);
    return ret;//这里只能返回ret,不能返回dp[fa][cur]
}

void solve(int n)
{
    REP(i,n)//以每个点为根往下递归一次
    {
        root=i;
        dfs(i,i);
    }
}

int main()
{
    //freopen("in","r",stdin);
    int n,m;
    int x,y,c;
    while(cin>>n>>m && n+m)
    {
        init(n);
        while(m--)
        {
            scanf("%d%d%d",&x,&y,&c);
            g[x][y]=g[y][x]=c;
        }
        int sum=prime(n);
        solve(n);
        cin>>m;
        long long ans=0;
        REP(i,m)
        {
            scanf("%d%d%d",&x,&y,&c);
            if(mst[x][y]) ans+=sum-g[x][y]+min(c,dp[x][y]);//如果是树边,从替代边和增加后的边替代原边
            else ans+=sum;//不然就不用管了,直接加上
        }
        printf("%.4lf\n",(double)ans/m);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值