【NOIP2015模拟10.22】最小代价

5 篇文章 0 订阅
2 篇文章 0 订阅

前言

本来在比赛上就想到最小生成树了,但不相信这道题那么简单,然后就没有然后了。。。

题目

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

分析

这道题最麻烦的地方就是最终搞成的图有可能有很多个联通块。

增加一个点:0点,让0点连接所有的黑点,边权为0;
处理从S发到每个点的最短距离
题目要求最小的总距离,显然搞一遍最小生成树就可以了。

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const long long maxlongint=2147483747;
using namespace std;
long long b[710000][4],dis[710000],next[710000],last[710000],a[710000],fa[710000],ans,tot,n,m,v[710000],d[8000000];
bool bz[710000];
void q(long long l,long long r)
{
    long long i=l,j=r;
    long long mid=b[(l+r)/2][0],e;
    while(i<j)
    {
        while(b[i][0]<mid) i++;
        while(b[j][0]>mid) j--;
        if(i<=j)
        {
            e=b[i][0];
            b[i][0]=b[j][0];
            b[j][0]=e;
            e=b[i][1];
            b[i][1]=b[j][1];
            b[j][1]=e;
            e=b[i][2];
            b[i][2]=b[j][2];
            b[j][2]=e;
            i++;
            j--;
        }
    }
    if(i<r) q(i,r);
    if(l<j) q(l,j);
}
int spfa()
{
    long long head=0,tail=1,k;
    fill(dis,dis+n+m+1,maxlongint*3);
    fill(bz,bz+n+m+1,true);
    d[1]=0;
    dis[0]=0;
    while(head<tail)
    {
        k=d[++head];
        bz[k]=true;
        for(long long i=last[k];i;i=next[i])
        {
            if(dis[a[i]]>dis[k]+v[i])
            {
                dis[a[i]]=dis[k]+v[i];
                if(bz[a[i]])
                {
                    d[++tail]=a[i];
                    bz[a[i]]=false;
                }
            }
        }
    }
}
bool dg(long long x)
{
    long long i,j;
    for(i=last[x];i;i=next[i])
    {
        long long y=a[i];
        if(dis[x]+v[i]==dis[y])
        {
            dg(y);
            b[++tot][1]=x;
            b[tot][2]=y;
            b[tot][0]=v[i];
        }
    }
    return true;
}
long long get(long long x)
{
    if(x==fa[x]) return x;
    long long y=get(fa[x]);
    fa[x]=y;
    return y;
}
int kruskal()
{
    long long i,j,k,l,x,y;
    for(i=1;i<=tot;i++)
    {
        x=get(b[i][1]);
        y=get(b[i][2]);
        if(x!=y)
        {
            ans+=b[i][0];
            fa[y]=x;
        }
    }
}
int bj(long long x,long long y,long long k)
{
    fa[y]=y;
    next[++tot]=last[x];
    last[x]=tot;
    v[tot]=k;
    a[tot]=y;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    long long i,j,k,l,x,y;
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&x);
        if(x) bj(0,i,0);
    }
    for(i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&x,&y,&k);
        bj(x,y,k);
        bj(y,x,k);
    }
    spfa();
    tot=0;
    dg(0);
    q(1,tot);
    bool b1=maxlongint;
    kruskal();
    if(ans==0) cout<<"impossible";else
        cout<<ans;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值