题目传送门:
https://www.luogu.org/problemnew/show/P2243
解题思路:
\text{ \ \ \ \ }
判断有无解不用说了。
\text{ \ \ \ \ }
首先想到裸
s
p
f
a
spfa
spfa,以格点为点,若不用旋转则建权为0的边;若要旋转则建权为1的边。但网格图会卡
s
p
f
a
spfa
spfa,那么我们就要进行优化。
\text{ \ \ \ \ } 因为边权为1或0,所以我们可以利用双端队列 B F S BFS BFS,枚举所有相邻点,优先搜索增加代价为0的点,即将增加代价为0的点从队头入队;增加代价为1的点从队尾入队。
\text{ \ \ \ \ } 并且要注意应该扩展到了当前点才更新v数组,否则会先扩展非最优解,而最优解无法扩展。 所以,你可以入队,但我不一定选你。
\text{ \ \ \ \ } 这样一来,可以保证每次扩展到一处就是当前最优解。最开始是最优,又是先权少的路,单调下去。
\text{ \ \ \ } 大吼一声:我大双端队列法不需要记忆化,时复 O ( R ∗ C ) O(R*C) O(R∗C)。
{\ \ \ \ \ } 其实优先队列的解法也类似,不过它需要记忆化,因为堆顶的边权最小但总代价不一定最小。从而不够优秀。总时间1470ms,而双端队列只要229ms。
上代码:
#include<cstdio>
#include<queue>
using namespace std;
char st[510][510];
bool v[510][510];
int n,m;
int minn(int x,int y){ return x<y?x:y; }
struct node{int x,y,c;}now;
deque<node> q;
void check(int x,int y,int c,int sx,int sy,char s)
{
if(x>=0 && x<=n && y>=0 && y<=m && !v[x][y])
{
if(st[sx][sy]==s) q.push_front((node){x,y,c});
else q.push_back((node){x,y,c+1});
}
}
int bfs()
{
for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) v[i][j]=false;
while(!q.empty()) q.pop_front();
node s; s.x=0; s.y=0; s.c=0;
q.push_front(s);
int nx,ny;
while(!q.empty())
{
now=q.front(); q.pop_front();
int x=now.x , y=now.y;
if(v[x][y]) continue; v[x][y]=true;
//应该扩展到了才更新,否则会先扩展非最优解
if(x==n && y==m) return now.c;
check(x+1,y+1,now.c,x+1,y+1,'\\');
check(x-1,y-1,now.c,x,y,'\\');
check(x+1,y-1,now.c,x+1,y,'/');
check(x-1,y+1,now.c,x,y+1,'/');
/* 因为点为格点,而输入却为格子,需要它们的转换关系。
画图得此关系:格点+1->格子也+1 ; 格点-1 ,格子还是格子 */
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",st[i]+1);
if((n+m)%2!=0) printf("NO SOLUTION\n");
else printf("%d\n",bfs());
}
return 0;
}