题目背景
Elf 是来自Gliese 星球的少女,由于偶然的原因漂流到了地球上。在她无依无靠的时候,善良的运输队员Mark 和James 收留了她。Elf 很感谢Mark和James,可是一直也没能给他们帮上什么忙。
题目描述
有一天 Mark 和James 的飞行车没有办法启动了,经过检查发现原来是电路板的故障。飞行车的电路板设计很奇葩,如下图所示:
输入输出格式
输入格式:
输入文件包含多组测试数据。第一行包含一个整数T 表示测试数据的数目。
对于每组测试数据,第一行包含正整数 R 和C,表示电路板的行数和列数。
之后 R 行,每行C 个字符,字符是”/”和”\”中的一个,表示标准件的方向。
对于40% 的数据,R,C≤5。
对于 100% 的数据,R,C≤500,T≤5。
输出格式:
对于每组测试数据,在单独的一行输出一个正整数,表示所需的缩小旋转次数。
如果无论怎样都不能使得电源和发动机之间连通,输出 NO SOLUTION。
输入输出样例
输入样例#1:
1
3 5
\/\
\///
/\\
输出样例#1:
1
说明
样例的输入对应于题目描述中的情况。
只需要按照下面的方式旋转标准件,就可以使得电源和发动机之间连通。
题解
这道题网上的题解貌似不是很多,而且说法也不是很清楚
即使参考别人的代码自己也想了挺久的
这里把自己的想法写下来希望能对之后写这道题的同学有所帮助
首先我们需要理解题意,然后将它转化为我们所习惯的图,如下所示
对角线联通则边权为0,相反边权为1(需要花一费旋转)
不难发现变成了一道求最短路的图论题了(建图的时候记得多1行1列,但我没建)
最后题目的答案,就是从(1,1)到(R,C)的最短路
但是别急,因为这道题把普通的SPFA卡掉了
所以这里我采用双端队列(deque)去维护
将能直达的(边权为0的)边加入队首,不能直达的(边权为1的)边加入队尾
这样的好处呢,就是每次都可以优先遍历边权为0的边,从而达到优化的效果
至于其他的操作呢,都是大同小异,直接上代码把
#include<deque>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
deque<pair<int,int> > dq;
pair<int,int> t;
int T,n,m,dis[505][505];
char a[505][505];
void work(int x,int y,int d,int bj)
{
if(dis[x][y]>d+bj)
{
dis[x][y]=d+bj;
if(bj) dq.push_back(pair<int,int>(x,y));
else dq.push_front(pair<int,int>(x,y));
}
}
void BFS()
{
int x,y,now;
dq.clear();
dq.push_front(pair<int,int>(1,1));
memset(dis,0x7f,sizeof dis);
dis[1][1]=0;//多次数据记得初始化
while(!dq.empty())
{
t=dq.front();
dq.pop_front();
x=t.first,y=t.second;
now=dis[x][y];
if(x>1&&y>1) work(x-1,y-1,now,a[x-1][y-1]!='\\');
if(x>1&&y<=m) work(x-1,y+1,now,a[x-1][y]!='/');
if(x<=n&&y>1) work(x+1,y-1,now,a[x][y-1]!='/');
if(x<=n&&y<=m) work(x+1,y+1,now,a[x][y]!='\\');
}
printf("%d\n",dis[n+1][m+1]);
}
int main()
{
scanf("%d",&T);
for(int i=1;i<=T;++i)
{
scanf("%d%d",&n,&m);
for(int j=1;j<=n;++j) scanf("%s",a[j]+1);
if((n+m)%2)
{
printf("NO SOLUTION\n");
continue;
}
BFS();
}
return 0;
}