【对角线 双端队列$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();
}
posted @ 2020-11-01 18:22  Hyx'  阅读(68)  评论(0)    收藏  举报