最小生成树cf1245D

克鲁斯卡尔算法解决供电优化问题
本文介绍了一个利用克鲁斯卡尔算法解决城市供电优化问题的方法。在一个包含n个城市的二维坐标系中,每个城市可以选择自己供电或从其他城市获取电力。文章详细解释了如何构建一个虚点0,并通过克鲁斯卡尔算法找到最优供电方案,以最小化总成本。

传送门

简单讲解一下题意:在一个二维坐标上有n个城市(坐标表示),每个城市可以选择自己供电花费b[i],也可以选择来自其他城市的电花费c,求最小花销,每一个供电的城市序号以及连接路径!
赛时
脑子:我会了,快写,赶紧的!
手:不,你还不会,需要冷静。。。

一个比较好的思路:我们可以构建一个虚点0,每一个直接与0相连的城市都是自己供电的,其他的都是通过连接来得到电。
克鲁斯卡尔算法注意合并两个点的时候指向城市自己发电小的那一个城市,从而保证最后一定都指向0;

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N = 3e3+5;
const int mod = 1e9+7;
struct ab{
    int x,y;
}a[N];
int b[N],c[N];
struct edge{
    int to,from;
    ll w;
    bool operator < (const edge& x) const{
        return w<x.w;
    }
}ma[N*N];
int fa[N],x1[N],y1[N],z1[N];
ll sum[N];
int get(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=get(fa[x]);
}
int main()
{
    int n;
    scanf("%d",&n);
    int i,j,k;
    int cnt=0,cnt1=0,cnt2=0;
    for(i=1;i<=n;i++) scanf("%d %d",&a[i].x,&a[i].y);
    for(i=1;i<=n;i++) scanf("%d",&b[i]);
    for(i=1;i<=n;i++) scanf("%d",&c[i]);
    for(i=1;i<=n;i++) ma[++cnt] =(edge) {0,i,b[i]};
    for(i=0;i<=n;i++) fa[i]=i,sum[i]=0;
    b[0]=0;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            int p=a[i].x-a[j].x,q=a[i].y-a[j].y;
            if(p<0) p=-p;
            if(q<0) q=-q;
            ma[++cnt].w=1ll*(p+q)*(c[i]+c[j]),ma[cnt].from=i,ma[cnt].to=j;
        }
    }
    sort(ma,ma+1+cnt);
    ll ans=0,ans1=1;
    for(i=1;i<=cnt;i++)
    {
        int u=ma[i].to,v=ma[i].from;
        int g=get(v);
        if(u==0&&g!=0)
        {
            ans+=b[v];
            z1[++cnt2]=v;
            fa[g]=0;
            continue;
        }
        int p=get(u),q=get(v);
        if(p!=q)
        {
            ans+=ma[i].w;
            x1[++cnt1]=u,y1[cnt1]=v;
            if(b[p]>b[q])
            {
                fa[p]=q;
            }
            else fa[q]=p;
        }
    }
    printf("%lld\n%d\n",ans,cnt2);
    for(i=1;i<=cnt2;i++) printf("%d\n",z1[i]);
    printf("%d\n",cnt1);
    for(i=1;i<=cnt1;i++)
    {
        printf("%d %d\n",x1[i],y1[i]);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值