【对角线 双端队列$bfs$】 电路维修
传送门
题意
给定一个\(n\times m\)的网格图,每个格点是格点两条对角线中的一个方向即\(\backslash\)或\(/\),从当前对角线的一端可以\(0\)花费走到另一端,如果当前移动到的下一个格点对角线方向不正好相接,经过一次操作后,可以改变对角线的方向,求从左上角走到右下角的最少操作数,如果不能抵达输出\(NO\; SOLUSION\)
数据范围
\(1\leq n,m\leq 500\)
题解
双端队列做法:
-
结论:问题有解当\(n+m\)为偶数的时候
-
将整个图转化为一个边权为\(0\)或\(1\)的无向图
-
每个格点可以沿着对角线向四个方向进行扩充,如果对角线方向不对就增加距离
- 在队列中,如果当前改变了对角线,即有步数的增加就放到队列尾部,否则放到首部,保证队列内部的单调性
-
每一个格点以左上角的坐标表示,扩充时候和原先给定的对角线图有一定的差异
- 一一对应即可
\(Dijkstra\)做法:
- 根据初始给定的对角线方向建图,建图后跑一遍堆优化的\(Dijkstra\)即可
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define fi first
#define se second
#define mp make_pair
const int N=510;
int n,m;
char g[N][N];
bool st[N][N];
int dist[N][N];
int bfs(){
memset(st,0,sizeof st);
memset(dist,0x3f,sizeof dist);
int dx[4]={1,1,-1,-1},dy[4]={1,-1,1,-1};
int idx[4]={0,0,-1,-1},idy[4]={0,-1,0,-1};
char c[5]="\\//\\";
deque< pair<int,int> >q;
q.push_front(mp(0,0));
dist[0][0]=0;
while(q.size()){
pair<int,int> t=q.front();
q.pop_front();
if(t.fi==n&&t.se==m) return dist[t.fi][t.se];
if(st[t.fi][t.se]) continue;
st[t.fi][t.se]=1;
rep(i,0,3){
int tx=t.fi+dx[i],ty=t.se+dy[i];
if(tx<0||tx>n||ty<0||ty>m) continue;
int lx=t.fi+idx[i],ly=t.se+idy[i];
int add=(g[lx][ly]!=c[i]);
int d=dist[t.fi][t.se]+add;
if(d<=dist[tx][ty]){
dist[tx][ty]=d;
if(add) q.push_back(mp(tx,ty));
else q.push_front(mp(tx,ty));
}
}
}
}
void solve(){
scanf("%d%d",&n,&m);
rep(i,0,n-1) scanf("%s",g[i]);
if(n+m&1) puts("NO SOLUTION");
else printf("%d\n",bfs());
}
int main(){
int _;scanf("%d",&_);
while(_--) solve();
}

浙公网安备 33010602011771号