题解 | lxy的通风报信

lxy的通风报信

https://ac.nowcoder.com/acm/contest/87255/G

lxy的通风报信:原题链接

题意:

     给你n*m的地图,地图上有a个军队和b个敌人,求怎么样花费最少的总路径,将地图上所有的军队连接,如果能的话,输出最小花费,不能则输出No。

注:题目只给出n*m的地图,a和b均未给出

名称 含义
* 我方军队:要连接的点
# 敌方军队:该点不可以通过
. 可通行的点

做题思路:

     本题的数据很小加上要求最短路径总量,解决方法很显然是生成最小树,但用二维做最小生成树很麻烦,所以先找出军队并给予每个军队编号,转为一维。

定义和查找编号:

//定义二维矩阵中军队的编号和查找
int getid(int x,int y){
	//当有值时返回编号 
	if (g.count({x,y}))return g[{x,y}];
	//没有说明还在查找阶段,给予点编号并返回 
	int id=g.size()+1;
	g[{x,y}]=id;
	return id;
}

读取地图,判断地图上军队的个数:

	/*
        记录军队的坐标 
		vector<pair<int,int>>a;
    */
	cin >>n>>m;
	for (int i=1;i<=n;i++)
	{
		cin >>mp[i]+1;
		for (int j=1;j<=m;j++)
		{
			if (mp[i][j]=='*')
			{
            	//记录军队总量 
				a.push_back({i,j});
				getid(i,j);
				++top;
			}
		}
	}

     由此我们可以得到给个军队的坐标,并给予编号,此时生成最小数的编号有了,但点到点之间的距离还不知道,要找到一个军队到其他军队的最小路径,本文采用bfs进行查找。

//bfs部分 
//创建数据类型 
struct Index{
	int x,y,step;
};
//移动方式 
int dir[5][2]={{1,0},{-1,0},{0,1},{0,-1}};
//判断点是否在图内,是否碰壁,是否经过 
bool ismp(int x,int y){
	return x<1 || y<1 || x>n || y>m || mp[x][y]=='#' || vis[x][y]; 
}
//经典bfs,找当前点到每个军队之间的距离 
void bfs(int sx,int sy){
	//每次查找重新初始话化vis 
	for (int i=1;i<=n;i++)
	{
        for (int j=1;j<=m;j++)vis[i][j] = 0;
    }
    //以下套用bfs模板
	queue<Index>q;
	q.push({sx,sy});
	vis[sx][sy]=1;
	while(q.size())
	{
        Index now,next;
		now=q.front();
		q.pop();
		for (int i=0;i<4;i++)
		{
			next.x=now.x+dir[i][0];
			next.y=now.y+dir[i][1];
            next.step=now.step+1; 
			if (ismp(next.x,next.y))continue;
			vis[next.x][next.y]=1;
			//是否为军队的点,是的话加入待生成边 
			if (mp[next.x][next.y]=='*')
			{
                //添加的边为起始点到该点的距离
				add(getid(sx,sy),getid(next.x,next.y),next.step);
			} 
			q.push(next);
		}
	}
} 

     经过上述代码,即可得到每个点之间的最小距离,之后代入最小生成树的模板即可得到答案。

函数代码:

//生成最小树部分 
//定义数据类型 
struct Edge{
	int from,to,w;
	bool operator <(const Edge &t) const
	{
		return w<t.w;
	}
}edges[N*N];
//父类节点 
int f[N*N];
//建立连接边 
void add(int from, int to, int w) {
    edges[edge_cnt].from=from;
    edges[edge_cnt].to=to;
    edges[edge_cnt].w=w;
    ++edge_cnt;
}
//查找父类 
int find(int x){
	return f[x]==x?x:f[x]=find(f[x]);
}
//连接边 
void merge(int x,int y){
	int a=find(x);
	int b=find(y);
	if (a!=b)f[b]=a;
}

主代码部分:

	//最小生成树部分 
	sort(edges,edges+edge_cnt);
	//初始化节点 
	for (int i=1;i<=top;i++)f[i]=i;
	//cnt记录建立边的总数
	//ans记录花费的总数 
	int cnt=0,ans=0;
    //经典最小生成树
	for (int i=0;i<edge_cnt && cnt<top-1;i++)
	{
		if (find(edges[i].from)!=find(edges[i].to))
		{
			merge(edges[i].from,edges[i].to);
			ans+=edges[i].w;
			cnt++;
		}
	}

     本题其实不难,主要考点在于bfs和最小生成树的综合应用,但问题是代码很长,在写的时候思路很可能被扰乱,而一旦出错要耗很长时间去修改排查,以下是AC代码。

#include <bits/stdc++.h>
using namespace std; 

const int N=1e3+10;
int n,m;
//记录边的总数 
int edge_cnt=0;
//记录军队的总数 
int top=0;
//记录军队的编号 
map<pair<int,int>,int>g;
//记录军队的坐标 
vector<pair<int,int>>a;
char mp[N][N];
int vis[N][N];

//定义二维矩阵中军队的编号和查找
int getid(int x,int y){
	//当有值时返回编号 
	if (g.count({x,y}))return g[{x,y}];
	//没有说明还在查找阶段,给予点编号并返回 
	int id=g.size()+1;
	g[{x,y}]=id;
	return id;
}
//生成最小树部分 
//定义数据类型 
struct Edge{
	int from,to,w;
	bool operator <(const Edge &t) const
	{
		return w<t.w;
	}
}edges[N*N];
//父类节点 
int f[N*N];
//建立连接边 
void add(int from, int to, int w) {
    edges[edge_cnt].from=from;
    edges[edge_cnt].to=to;
    edges[edge_cnt].w=w;
    ++edge_cnt;
}
//查找父类 
int find(int x){
	return f[x]==x?x:f[x]=find(f[x]);
}
//连接边 
void merge(int x,int y){
	int a=find(x);
	int b=find(y);
	if (a!=b)f[b]=a;
}
//bfs部分 
//创建数据类型 
struct Index{
	int x,y,step;
};
//移动方式 
int dir[5][2]={{1,0},{-1,0},{0,1},{0,-1}};
//判断点是否在图内,是否碰壁,是否经过 
bool ismp(int x,int y){
	return x<1 || y<1 || x>n || y>m || mp[x][y]=='#' || vis[x][y]; 
}
//经典bfs,找当前点到每个军队之间的距离 
void bfs(int sx,int sy){
	//每次查找重新初始话化vis 
	for (int i=1;i<=n;i++)
	{
        for (int j=1;j<=m;j++)vis[i][j] = 0;
    }
	queue<Index>q;
	q.push({sx,sy});
	vis[sx][sy]=1;
	while(q.size())
	{
        Index now,next;
		now=q.front();
		q.pop();
		for (int i=0;i<4;i++)
		{
			next.x=now.x+dir[i][0];
			next.y=now.y+dir[i][1];
            next.step=now.step+1;
			//是的话跳过 
			if (ismp(next.x,next.y))continue;
			vis[next.x][next.y]=1;
			//是否为军队的点,是的话加入待生成边 
			if (mp[next.x][next.y]=='*')
			{
                //添加的边为起始点到该点的距离
				add(getid(sx,sy),getid(next.x,next.y),next.step);
			} 
			q.push(next);
		}
	}
} 

int main(){
	cin >>n>>m;
	for (int i=1;i<=n;i++)
	{
		//记录军队有几个 
		cin >>mp[i]+1;
		for (int j=1;j<=m;j++)
		{
			if (mp[i][j]=='*')
			{
				a.push_back({i,j});
				getid(i,j);
				++top;
			}
		}
	}
	//将军队的每个点放入bfs查找该点到其余点的距离 
	for (auto &i:a)bfs(i.first,i.second);
	//最小生成树部分 
	sort(edges,edges+edge_cnt);
	//初始化节点 
	for (int i=1;i<=top;i++)f[i]=i;
	//cnt记录建立边的总数
	//ans记录花费的总数 
	int cnt=0,ans=0;
    //经典最小生成树
	for (int i=0;i<edge_cnt && cnt<top-1;i++)
	{
		if (find(edges[i].from)!=find(edges[i].to))
		{
			merge(edges[i].from,edges[i].to);
			ans+=edges[i].w;
			cnt++;
		}
	}
	if (cnt<top-1)cout <<"No\n";
	else cout <<ans<<"\n";
    return 0;
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
05-29 22:21
Offer1:小马智行,深圳,测试开发工程师,17.0k*16.0,Offer2:追觅科技,深圳,嵌入式工程师,18.0k*15.0,
嵌软狗都不学:各位base深圳的同事,作为也是并肩作战的一员,今天想站在管理视角,和大家开诚布公地聊一聊:从近几个月的上下班数据对比看来,我们发现一个明显的差异:深圳同事的在岗时间普遍比苏州同事短。很多深圳同事早上9点之后才到公司,晚上不到 20 点就下班了;而总部那边,20点半甚至 22 点后还有不少同事在办公室忙碌,特别是研发团队,加班更是常态。相信去过苏州的同事,对这种场景都不陌生。我很好奇,这是因为苏州工作任务太重还是咱们深圳同事效率真的高到能在更短时间内完成工作?MOVA在深圳成立分公司是为了吸引更优秀的人才贡献更多更高质的价值,公司管理层给我反馈的是深圳招到的多是行业的专家大拿,大部分都是薪资比苏州高的,而且我们办公的租金等也远高于苏州的..MOVA虽脱胎于强壮的集团母体不久,各业务板块尚未实现全面盈利,虽说公司管理层目光长远,不纠结当下的人才投入,但行业内的普遍标准是,员工创造的价值要达到公司雇佣成本的 15 倍以上。大家不妨自我审视一下,自己是否达到了这个标准?如果是抱着划水、按时打卡走人拿毛爷爷的心态那不适合来MOVA,那样过下去不但自己过得尴尬也会影响MOVA这个大船的攻城略地的速度.我并非鼓励大家盲目加班,而是倡导高效工作,拒绝无效忙碌,不要让项目进度因低效受影响,也别把精力浪费在和苏州同事拼打卡时长上,提倡更高的人效比;考虑到两地地域和交通差异,相信大家会找最适合自己发挥的工作方式(比如按时下班后1小时到家晚饭后继续未竟工作等..)大家在遵守公司规章的情况下尽情地体现自己的能力价值,为MOV!和深圳公司争光我们在这边才能更安心更有信心的工作下去;请客BU长、名部门长、项目管理和各业务单元负责人,全面梳理团队情况,及时评估成员工作负荷与成果质量,坚决清退划水害虫痕疫,践行公司价值观,相互监督,防止管理漏洞及渎职。感谢人家的理解,也请人家多担待我的直言不讳……
点赞 评论 收藏
分享
05-20 13:59
门头沟学院 Java
米黑子米黑子:你这个成绩不争取下保研?
点赞 评论 收藏
分享
评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务