题目大意:
题目链接:https://jzoj.net/senior/#main/show/5353
为了加快社会主义现代化,建设新农村,农夫约(Farmer Jo)决定给农庄里每座建筑都连上互联网,方便未来随时随地网购农药。
他的农庄很大,有N 座建筑,但地理位置偏僻,网络信号很差。
一座建筑有网,当且仅当满足以下至少一个条件:
1、给中国移动交宽带费,直接连网,花费为A。
2、向另外一座有网的建筑,安装共享网线,花费为B×两者曼哈顿距离。
现在,农夫约已经统计出了所有建筑的坐标。他想知道最少要多少费用才能达到目的。
思路:
首先有一点很明显的。如果最终的网络形成了个联通块,那么就必须交的钱。
那么对于一个已经有部分网络共享线的图,倘若连接和,那么肯定要满足一下两个要求才会最优:
- 和位于两个不同联通块内。否则根本没必要连。
- ,否则直接分别在这两个联通块上联网即可。
发现没,这正是一个最小生成树。
用,当现在最小的边所需要的费用大于之后停止连边,此时的网线及为最优解。
代码:
#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
",sum+s*A);
return 0;
}