搭桥(CODEVS 1002),详细注释

说明

考查最小生成树,这里用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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值