ACWing175. 电路维修 二解(bfs 与建图求最短路)

题目传送门:https://www.acwing.com/problem/content/177/
【题目大意】
达达是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女翰翰,从而被收留在地球上。翰翰的家里有一辆飞行车。有一天飞行车的电路板突然出现了故障,导致无法启动。电路板的整体结构是一个R行C列的网格(R,C≤500),如下图所示。
在这里插入图片描述每个格点都是电线的接点,每个格子都包含一个电子元件。电子元件的主要部分是一个可旋转的、连接一条对角线上的两个接点的短电缆。在旋转之后,它就可以连接另一条对角线的两个接点。电路板左上角的接点接入直流电源,右下角的接点接入飞行车的发动装置。达达发现因为某些元件的方向不小心发生了改变,电路板可能处于断路的状态。她准备通过计算,旋转最少数量的元件,使电源与发动装置通过若干条短缆相连。不过,电路的规模实在是太大了,达达并不擅长编程,希望你能够帮她解决这个问题。
【输入格式】
输入文件包含多组测试数据。
第一行包含一个整数T,表示测试数据的数目。
对于每组测试数据,第一行包含正整数R和C,表示电路板的行数和列数。
之后R行,每行C个字符,字符是"/“和”"中的一个,表示标准件的方向。
【输出格式】
对于每组测试数据,在单独的一行输出一个正整数,表示所需的缩小旋转次数。
如果无论怎样都不能使得电源和发动机之间连通,输出NO SOLUTION。
【数据范围】
1≤R,C≤500,
1≤T≤5
【输入样例】:
1
3 5
\/\
\///
/\\
【输出样例】:
1
【样例解释】
样例的输入对应于题目描述中的情况。
只需要按照下面的方式旋转标准件,就可以使得电源和发动机之间连通。
在这里插入图片描述
分析:
方法一:建图+堆优化dijkstra求最短路
  题目中旋转短电缆是需要花费1的代价的,旋转后是另外两个点的连接,那么我们可以根据读入的短电缆方向确定格点上边权为0的无向边,那么与其方向垂直的方向的格点可建立边权为1的无向边。那么会构成一个由点集0~(R+1)(C+1)-1组成的无向图,求解0为起点,(R+1) * (C+1)-1为终点的最短路,边权为0或者1,因此可采用dijkstra求最短路,考虑到普通的dij时间复杂度为O(N^2),其中N=501501(格点数),会超时,那么考虑使用堆优化的dij,时间复杂度为O(NlogN)可以通过。
注意建立图时分析好格点与格子坐标关系
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 500+6;
int r,c;
char s[MAXN][MAXN];
struct node {
	int u,v,w,next;
};
int d[MAXN*MAXN];
bool vis[MAXN*MAXN];
int cnt, head[MAXN*MAXN];
node edge[MAXN*MAXN*4];  //一个格子里会有四条边 
void init(){
	memset(head,0,sizeof(head));
	memset(edge,0,sizeof(edge));
	memset(s,0,sizeof(s));
	cnt = 0;
}
void read(){
	cin >> r >> c;
    for(int i= 0; i< r; i++) scanf("%s",s[i]);
} 
void addedge(int u,int v,int w){
	edge[++cnt]=(node){u,v,w,head[u]};
	head[u] = cnt;
}
void pre_edge(){
	int xc = c+1;
	for(int i = 0; i< r; i++){
		for(int j = 0; j< c; j++){
			if(s[i][j] == '/'){	 //将(i,j)坐标的格子边上四个点对应建立无向边,构成一个图。		
				addedge(xc*i+j, xc*(i+1)+j+1 , 1);
				addedge( xc*(i+1)+j+1 ,xc*i+j,  1);
				addedge(xc * i + j +1 ,xc*(i+1)+j , 0);
				addedge(xc *(i+1)+j ,xc * i + j +1, 0);
			}
			else{			
				addedge(xc*i+j, xc*(i+1)+j+1 , 0);
				addedge( xc*(i+1)+j+1 ,xc*i+j,  0);
				addedge(xc * i + j +1 ,xc*(i+1)+j , 1);
				addedge(xc *(i+1)+j ,xc * i + j +1 , 1);
			}
		}
	}
}
void dij(){  //堆优化的dijkstra求最短路 
	memset(d,0x3f,sizeof(d));
	memset(vis,0,sizeof(vis));
	priority_queue < pair<int,int> >q;
	int ed = (c+1)*r+c;
	d[0] = 0;
	q.push(make_pair(0,0));
	while(!q.empty()){
		int u = q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u] = true;
		for(int i = head[u];i; i = edge[i].next){
			int v = edge[i].v;
			int w = edge[i].w;
			if(d[u] + w < d[v]){
				d[v] = d[u] + w;
				q.push(make_pair(-1*d[v],v));
			}
		}		
	}
}
int main(){
    int t;
    cin >> t;
    while(t--){
    	init();
    	read();
    	pre_edge();
		dij();
    	int step = d[(c+1)*r+c] ;
    	if(step == 0x3f3f3f3f) printf("NO SOLUTION\n");
		else printf("%d\n",step); 
    }
    return 0;
}

方法二:ZYX写的宽搜,在此记录,用双端队列,如果这条分支时边权为0,就沿着该分支到达的新节点从队头入队,如果这条分支是边权为1的边,就从普通宽搜一样从队尾入,思路详见代码中注释。

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

const int Maxn=505;
const int gzx[4]={0,0,1,1},gzy[4]={0,1,1,0};
const int gz[4]={0,1,0,1},dx[4]={-1,-1,1,1},dy[4]={-1,1,1,-1};

int r,c;
int Map[Maxn][Maxn];//每一个格子的电缆 0:\ 1:/
int minstep[Maxn][Maxn];
bool vis[Maxn][Maxn];
//起点坐标为(0,0) 终点坐标为(r,c)

struct node{
    int x,y,cnt;
};

bool check(int x,int y){
    if(x<0||y<0||x>r||y>c)return false;
    return true;
}

void work(){
    deque<node> q;
    memset(vis,0,sizeof(vis));
    memset(minstep,0x3f,sizeof(minstep));
    q.push_front((node){0,0,0});
    while(!q.empty()){
        node temp=q.front();
        q.pop_front();
        int x=temp.x,y=temp.y,cnt=temp.cnt;
        if(vis[x][y])continue;
        vis[x][y]=true;
        if(x==r && y==c){printf("%d\n",cnt);return;}
        for(int i=0;i<4;i++){
            int gx=x+gzx[i],gy=y+gzy[i];
            if(gx>0 && gy>0 && gx<=r && gy<=c){
                int nx=x+dx[i],ny=y+dy[i];
                if(check(nx,ny)){
                	if(minstep[nx][ny]==0x3f3f3f3f){
                		if(Map[gx][gy]!=gz[i]){q.push_back((node){nx,ny,cnt+1});minstep[nx][ny]=cnt+1;} 
                   		else{q.push_front((node){nx,ny,cnt});minstep[nx][ny]=cnt;}
                	}
                    else if(Map[gx][gy]==gz[i] && cnt<minstep[nx][ny]){
                    	q.push_front((node){nx,ny,cnt});
                    }
                }
            }
        }
    }  
    printf("NO SOLUTION\n");
}

int main(){
    //可以理解成从起点走向不同的格点 中途可以更改道路,最后走到终点
    //那么过程中就要记录当前更改道路的次数,和宽搜一样,保证了到达一个格点时,一定是用了最少的更改道路
    //然后还要注意一个细节:从一个点向其他点行走时不一定要更改道路,也就是说更改道路次数不一定都是递增的,所以要用双端队列维护队列单调性
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&r,&c);
        for(int i=1;i<=r;i++){
            for(int j=1;j<=c;j++){
                char c=getchar();
                while(c!='\\'&&c!='/')c=getchar();
                if(c=='\\')Map[i][j]=0;
                else Map[i][j]=1;
                //将字符转化为数字
            }
        }    
        work();
    }
    return 0;
}

发布了88 篇原创文章 · 获赞 22 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/xuechen_gemgirl/article/details/95060527