[51Nod 1443] 路径和树

25 篇文章 0 订阅
19 篇文章 0 订阅

Description

给定一幅无向带权连通图G = (V, E) (这里V是点集,E是边集)。从点u开始的最短路径树是这样一幅图G1 = (V, E1),其中E1是E的子集,并且在G1中,u到所有其它点的最短路径与他在G中是一样的。
现在给定一幅无向带权连通图G和一个点u。你的任务是找出从u开始的最短路径树,并且这个树中所有边的权值之和要最小。
n,m<=300000

Solution

首先把跑一遍最短路,把最短路上的边标出来构成最短路图,明显它是有向无环的

大贪心:除起点外每个点选择边权最小的一条入边,加起来就是答案

证明:
为什么这是最优的?
最短路径树中每个点有且仅有一个祖先,每个点都选边权最小的一条一定是最优的

为什么这是一棵树?
首先我们选了n-1条边,又因为这是一个有向无环图,不可能构成环,所以它必定是一个连通图,也就是一棵树

Code

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
#define LL long long
using namespace std;
int fs[N],nt[2*N],dt[2*N],pr[2*N],s1[N],n,m,m1,st,d[5*N];
LL dis[N];
bool bz[N];
void link(int x,int y,int z)
{
    nt[++m1]=fs[x];
    dt[fs[x]=m1]=y;
    pr[m1]=z;
}
void spfa()
{
    int l=0,r=1;
    d[r]=st;
    memset(dis,107,sizeof(dis));
    dis[st]=0;
    bz[st]=1;
    while(l<r)
    {
        int k=d[++l];
        for(int i=fs[k];i;i=nt[i])
        {
            int p=dt[i];
            if(dis[k]+pr[i]<dis[p])
            {
                dis[p]=dis[k]+pr[i];
                if(!bz[p]) d[++r]=p,bz[p]=1;
            }
        }
        bz[k]=0;
    }
}
void bfs()
{
    int l=0,r=1;
    d[r]=st;
    memset(bz,0,sizeof(bz));
    bz[st]=1;
    while(l<r)
    {
        int k=d[++l];
        for(int i=fs[k];i;i=nt[i])
        {
            int p=dt[i];
            if(dis[k]+pr[i]==dis[p]) 
            {
                s1[p]=min(s1[p],pr[i]);
                if(!bz[p]) d[++r]=p,bz[p]=1;
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    fo(i,1,m)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        link(x,y,z),link(y,x,z);
    }
    cin>>st;
    memset(s1,107,sizeof(s1));
    spfa();
    bfs();
    LL v=0;
    fo(i,1,n) if(i!=st) v+=s1[i];
    printf("%lld\n",v);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值