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

版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/85393238

题目大意:

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


思路:

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

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

发现没,这正是一个最小生成树。
K r u s k a l Kruskal ,当现在最小的边所需要的费用大于 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;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/85393238
今日推荐