说明
考查最小生成树,这里用Kruskal算法实现,里面用到了并查集。
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m=0;//行列数
char input[2505];//输入的数据
struct bcj {
//并查集
int f[2505];
//初始化
void init() {
for(int i=1; i<=n*m; i++)
f[i]=i;
}
//找爸爸的递归函数,不停去找,直到找到祖宗为止
int getf(int x) {
if(f[x]==x) {
return x;
} else {
f[x]=getf(f[x]);//路径压缩
return f[x];
}
}
//合并两个子集
int merge(int x,int y) {
int t1=getf(x);
int t2=getf(y);
if(t1!=t2) {
f[t2]=t1;
return 1;
}
return 0;
}
} b;
struct coord {
int x,y;//计算每个建筑物行列坐标
} coo[2505];
struct dist {
int u,v,w;//u是左建筑,v是右建筑,w是两个建筑之间的距离
} dis[6250005];//最多有2500个建筑物,所以最多有2500*2500条边
bool comp(const dist &a,const dist &b) {
return a.w<b.w;//用于sort函数的第三个参数,从小到大排序
}
int main() {
cin>>n>>m;
int temp_x=1;
int temp_y=1;
int temp_city=0;//计算有多少个#
int temp_area=0;//计算有多少个.
//开始计算每个建筑物在第几行第几列
//coo[i].x是指i元素在第几行,coo[i].y是指i元素在第几列 ,并不是直角坐标系
for(int i=1; i<=n*m; i++) {
cin>>input[i];
if(input[i]=='.') {
temp_area++;//空地数加一
}
if(input[i]=='#') {
temp_city++;////建筑物数加一
coo[i].x=temp_x;
coo[i].y=temp_y;
}
temp_y++;//列++
if(temp_y==m+1) {
temp_y=1;//这一行已经走完了,换下一行开始
temp_x++;//行++
}
}
//开始计算建筑物之间的距离
int pos=1;
for(int i=1; i<=n*m-1; i++) {
if(input[i]=='#') {
for(int j=i+1; j<=n*m; j++) {
if(input[j]=='#') {
dis[pos].u=i;//记录这条边的左建筑物
dis[pos].v=j;//记录这条边的右建筑物
int t1=abs(coo[i].x-coo[j].x);//t1是x1-x2的绝对值
int t2=abs(coo[i].y-coo[j].y);//t2是y1-y2的绝对值
if(t1<=1&&t2<=1) {
dis[pos].w=0;//同一建筑物之间距离为0,记录边的长度
}
if(t1>1&&t2>1) {
dis[pos].w=-1;//两个建筑物无法用桥搭建为-1,记录边的长度
}
if(t1<=1&&t2>1) {
dis[pos].w=t2-1;//记录边的长度
}
if(t2<=1&&t1>1) {
dis[pos].w=t1-1;//记录边的长度
}
pos++;
}
}
}
}
sort(dis+1,dis+pos,comp);//把所有边根据w(长度)进行从小到大排序
b.init();//并查集初始化
int bridge_len=0;//桥的总长度
int bridge_sum=0;//桥的数目
int count=0;//提前退出循环的标记
int t=0;
//遍历所有边,把符合条件的进行合并
for(int i=1; i<pos; i++) {
if(dis[i].w!=-1) {
if(b.merge(dis[i].u,dis[i].v)) {
count++;
if(dis[i].w>0) {
bridge_sum++;
bridge_len+=dis[i].w;
}
}
}
if(count==temp_city-1)
break;
}
//下面开始计算建筑物数
b.init();//重新初始化
for(int i=1; i<pos; i++) {
if(dis[i].w==0) {
b.merge(dis[i].u,dis[i].v);
}
}
for(int i=1; i<=n*m; i++) {
if(b.f[i]==i) {
t++;//此时t是建筑物数加上空地数,最后输出建筑物时,减去空地数就可以了
}
}
cout<<t-temp_area<<endl ;
cout<<bridge_sum<<" "<<bridge_len<<endl;
return 0;
}