【JZOJ5353】【NOIP2017提高A组模拟9.9】村通网【最小生成树】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/5353
为了加快社会主义现代化,建设新农村,农夫约(Farmer Jo)决定给农庄里每座建筑都连上互联网,方便未来随时随地网购农药。
他的农庄很大,有N 座建筑,但地理位置偏僻,网络信号很差。
一座建筑有网,当且仅当满足以下至少一个条件:
1、给中国移动交宽带费,直接连网,花费为A。
2、向另外一座有网的建筑,安装共享网线,花费为B×两者曼哈顿距离。
现在,农夫约已经统计出了所有建筑的坐标。他想知道最少要多少费用才能达到目的。


思路:

首先有一点很明显的。如果最终的网络形成了 k k k个联通块,那么就必须交 A × k A\times k A×k的钱。
那么对于一个已经有部分网络共享线的图,倘若连接 x x x y y y,那么肯定要满足一下两个要求才会最优:

  • x x x y y y位于两个不同联通块内。否则根本没必要连。
  • d i s [ x ] [ y ] × B ≤ A dis[x][y]\times B\leq A dis[x][y]×BA,否则直接分别在这两个联通块上联网即可。

发现没,这正是一个最小生成树。
K r u s k a l Kruskal Kruskal,当现在最小的边所需要的费用大于 A A A之后停止连边,此时的网线及为最优解。


代码:

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;

const int N=1010;
int n,A,B,x[N],y[N],father[N],sum,s,u,v,tot;
bool vis[N];

struct edge
{
	int from,to,dis;
}e[N*N];

void add(int from,int to,int dis)
{
	e[++tot].to=to;
	e[tot].from=from;
	e[tot].dis=dis;
}

bool cmp(edge x,edge y)
{
	return x.dis<y.dis;
}

int find(int x)
{
	return x==father[x]?x:find(father[x]);
}

int main()
{
	freopen("pupil.in","r",stdin);
	freopen("pupil.out","w",stdout);
	scanf("%d%d%d",&n,&A,&B);
	for (int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		father[i]=i;
	}
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			add(i,j,abs(x[i]-x[j])+abs(y[i]-y[j]));  //求出任意两点之间的曼哈顿距离
	sort(e+1,e+1+tot,cmp);
	for (int i=1;i<=tot;i++)  //Kruskal
	{
		u=e[i].from;
		v=e[i].to;
		if (e[i].dis*B>A) break;  //连接更优才继续
		if (find(u)!=find(v))
		{
			father[find(u)]=find(v);
			sum+=e[i].dis*B;  //连接所需的费用
		}
	}
	for (int i=1;i<=n;i++)  //求联通块个数,后来发现大可不必这么麻烦
		if (!vis[find(i)])  //没有记录过这个联通块
		{
			vis[find(i)]=1;
			s++;
		}
	printf("%d\n",sum+s*A);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值