1448:【例题1】电路维修
1448:【例题1】电路维修
样例
样例输入 样例输出
3 5 1
\\/\\
\\///
/\\\\
题解
我们可以把电路板上的每个格点(横线与竖线的交叉点)看作无向图中的结点。
若两个结点x和y是某个小方格的两个对角,则在x与y之间连边。若该方格中的标准件(对角线)与x到y的线段重合,则边权为0;若垂直相交,则边权为1(说明需要旋转1次才能连通)。然后,我们在这个无向图中求出从左上角到右下角的最短距离,就得到了结果。
这是一个边权要么是0,要么是1的无向图。在这样的图上,我们可以通过双端队列广度搜索计算。
分支边权为1,从队尾入
分支边权为0,从队首入
保证两段性和单调性
因为每个节点只需要访问一次,所以算法的时间复杂度为O(R×C)
注释:
1. make_pair的用法
无需写出型别, 就可以生成一个pair对象
例: make_pair(42,‘%’);
而不必费力写成: pair<int, char>(42,‘%’)
2. if((r+c)%2) 那么无解
画个图理解一下就好了,毕竟你是要走对角线
代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<queue> #include<functional> #include<cstdlib> #include<utility> using namespace std; const int cap=500010; int dist[510][510]; //两点间的最短距离 char map[510][510]; //读图 pair<int,int> q[cap*2]; //队列 int r,c,li,ri; inline bool valid(int x,int y) //是否出界 { return x>=0&&x<=r&&y>=0&&y<=c; } inline void que_add(int x,int y,int v) { if(dist[x][y]<0||v<dist[x][y]) { dist[x][y]=v; if(li==ri||v>dist[q[li].first][q[li].second]) q[ri++]=make_pair(x,y); //make_pair是生成pair类型的数据 注释1 else q[--li]=make_pair(x,y); } } int main() { int kase; for(scanf("%d",&kase);kase;--kase) { scanf("%d %d",&r,&c); if((r+c)%2) //注释2 { for(int i=0;i<r;i++) scanf("%s",map[i]); printf("NO SOLUTION\n"); } else { for(int i=0;i<r;i++) scanf("%s",map[i]); li=ri=cap; q[ri++]=make_pair(0,0); memset(dist,-1,sizeof(dist)),dist[0][0]=0; //初始化距离无穷大 while(li!=ri) //队列非空 { int cx=q[li].first,cy=q[li].second; ++li; if(valid(cx-1,cy-1)) { if(map[cx-1][cy-1]=='\\') que_add(cx-1,cy-1,dist[cx][cy]); else que_add(cx-1,cy-1,dist[cx][cy]+1); } if(valid(cx-1,cy-1)) { if(map[cx-1][cy]=='/') que_add(cx-1,cy+1,dist[cx][cy]); else que_add(cx-1,cy+1,dist[cx][cy]+1); } if(valid(cx+1,cy-1)) { if(map[cx][cy-1]=='/') que_add(cx+1,cy-1,dist[cx][cy]); else que_add(cx+1,cy-1,dist[cx][cy]+1); } if(valid(cx+1,cy+1)) { if(map[cx][cy]=='\\') que_add(cx+1,cy+1,dist[cx][cy]); else que_add(cx+1,cy+1,dist[cx][cy]+1); } } printf("%d\n",dist[r][c]); } } return 0; }