【NOIP模拟】黑白图

题面

 一个 n 个点 m 条边构成的无向带权图。由一些黑点与白点构成 树现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个,可以选 取其中任意一个),我们想要使得花费的代价最小。请问这个最小代价是多少? 注意:最后选出的边保证每个白点到黑点的距离任然是最短距离。

分析

这道题考的时候写的比较久,结果思路完全错误..看着黑白就觉得像一道叫位图的题,问的是每个黑点到白点的距离,做法是将所有白点同时扩展,但是那是个无权图啊,我抖机灵如法炮制,挂到窒息。

看到正解不是很意外,核心思想就是加了一个超级源。

因为此题的难度在于如何处理多源最短路。将超级源和每个黑点连一条权值为0的边,以超级源为起点跑spfa或者dijstra,就能等效得出白点到最近的黑点的距离了。

统计这些在最近路径上的边只需要遍历一遍,显然,满足d[u]+w==d[v]的边就在最短路径上,再将这些边抽出来,在新图上跑最小生成树。

因为无论留下的道路是哪一条,对于任意白点,只要能到达黑点,那一定是最短的,因为我们本来留下的边就是最短路径。最小生成树能保证图的连通,并更优化边集的选择。所以就是两个板套在一起,就看能不能想到了。。

注意long long

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
ll n,m,cnt=0,cot=0,num=0,ans=0,sup=0;
ll first[N],col[N],fa[N],d[N],black[N],vis[N];
struct email
{
    ll u,v,w;
    ll nxt;
}e[N*5],a[N*5];
queue<ll>q;
inline ll find(ll x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

bool cmp(email a,email b)
{
    return a.w<b.w;
}

inline void add(ll u ,ll v,ll w)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;e[cnt].w=w;
}

inline void rebuild(ll u ,ll v,ll w)
{
    a[++cot].u=u;a[cot].v=v;a[cot].w=w;
}

void spfa(ll x)
{
    q.push(x);vis[x]=1;d[x]=0;
    while(!q.empty())
    {
        ll u=q.front();q.pop();vis[u]=0;
        for(ll i=first[u];i;i=e[i].nxt)
        {
            ll v=e[i].v,w=e[i].w;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}
void kruskal()
{
    for(ll i=1;i<=n;i++)
        fa[i]=i;
    sort(a+1,a+1+cot,cmp);
    for(ll i=1;i<=cot;i++)
    {
        ll u=a[i].u,v=a[i].v,w=a[i].w;
        ll fax=find(u),fay=find(v);
        if(fax!=fay)
        {
            fa[fax]=fay;
            ans+=w;num++;
        }
        if(num==n-1)
            break;
    }
}

int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&col[i]);
        if(col[i])add(sup,i,0);
        d[i]=INF;
    }    
    for(ll i=1;i<=m;i++)
    {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);add(v,u,w);
    }
    spfa(sup);
    for(ll i=1;i<=n;i++)
        if(d[i]==INF)
        {
            printf("impossible
");
            return 0;
        }
    for(ll i=1;i<=n;i++)
        for(ll p=first[i];p;p=e[p].nxt)
        if(e[p].w+d[i]==d[e[p].v])
            rebuild(e[p].u,e[p].v,e[p].w);        
    kruskal();
    printf("%lld
",ans);
    return 0;
}
“Make my parents proud,and impress the girl I like.”
原文地址:https://www.cnblogs.com/NSD-email0820/p/9458129.html