1930. 灌溉农田(irrigation)
题目描述
由于最近缺少降雨,农夫约翰决定在他的N块农田之间建立一个供水管网。每块的位置可以用一个二维坐标来表示(xi,yi),在第i块地和第j块地之间修建一个管道的话,代价是(xi - xj)^2 + (yi - yj)^2。农夫约翰想要建立一个花费代价最小的供水管网,使得他所有的地都能被连接在一起(使得水能够通过一系列的管道流到各个田地里去)。不幸的是,建造管道的人拒绝建造花费代价小于C的单条管道。请帮助约翰计算最少需要花费多少代价,才能建成这个供水管网。
输入
第一行是两个正整数N和C。
第2行到第N+1行,每行两个整数,表示xi和yi。
输出
输出建立供水管网的最小代价,如果不能建立供水管网,就输出-1。
样例输入
3 11
0 2
5 0
4 3
样例输出
46
数据范围限制
1<=N<=2000,0<=xi,yi<=1000。
提示
样例中,约翰不能在(4,3)和(5,0)之间建立管道,因为这个管道的代价是10。因此,他只能在(0,2)和(5,0)之间修建一条管道,花费是29,在(0,2)和(4,3)之间修建一条管道,花费是17,所以总的最小花费是29+17=46。
思路:
这是一道典型的最小生成树。因为农夫想要代价最小,所以只需n-1条边,然后保证n-1条边权和最小。故可以用最小生成树。
唯一考虑只有边权>=花费代价C,才能加边。
#include<cstdio>
#include<iostream>
#include<cstring>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int INF=2147483647;
long long n,m,a[2010],b[2010],c[2010][2010],lowcost[2010],ans,ds;
bool vis[2010];
void input()
{
memset(c,127,sizeof(c));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
for(int j=1;j<i;j++)
{
int t=(a[i]-a[j])*(a[i]-a[j])+(b[i]-b[j])*(b[i]-b[j]);
if(t>=m) c[i][j]=c[j][i]=t;
}
}
}
void Prim()
{
int v=1;
vis[v]=1;
for(int i=1;i<=n;i++) lowcost[i]=c[v][i];
for(int i=1;i<n;i++)
{
int temp=INF,t;
for(int j=1;j<=n;j++)
if(lowcost[j]<temp&&!vis[j])
temp=lowcost[j],t=j;
if(temp==INF) continue;
vis[t]=1,v=t,ans+=temp,ds++;
for(int j=1;j<=n;j++)
if(lowcost[j]>c[v][j]&&!vis[j])
lowcost[j]=c[v][j];
}
}
int main()
{
fre(irrigation);
input();
Prim();
if(ds==n-1) printf("%d",ans);
else printf("-1");
return 0;
}