题解 P2208 【[USACO13OPEN]重力异常What's Up With Gra…】

· · 题解

这题可以通过构建分层图的方式解决

构建两个图,分别是重力向上的和重力向下的

对于重力某种情况下可以反转重力的位置,在该位置向另一张图连权为1的单向边

对于某种重力状况下可以向左或向右的位置,向左或向右连0边

跑最短路,在终点的两种情况下取最短距离即可

#include"cstdio"
#include"cstring"
#include"iostream"
#include"algorithm"
using namespace std;

const int MAXN=1005;

int n,m,np,S,T;
int ln[MAXN*MAXN<<1],h[MAXN*MAXN<<1];
int q[MAXN*MAXN<<2];
bool vis[MAXN*MAXN<<1];
bool c[MAXN][MAXN];
struct rpg{
    int li,nx,ln;
}a[MAXN*MAXN<<2];

int get(int x,int y){return (x-1)*m+y;}
void add(int ls,int nx,int ln){a[++np]=(rpg){h[ls],nx,ln};h[ls]=np;}

void SPFA(int S)
{
    memset(ln,0x7f,sizeof(ln));
    int hd=1,tl=1;q[hd]=S;ln[S]=0;vis[S]=1;
    while(hd<=tl){
        int nw=q[hd++];vis[nw]=0;
        for(int i=h[nw];i;i=a[i].li){
            if(ln[a[i].nx]>ln[nw]+a[i].ln){
                ln[a[i].nx]=ln[nw]+a[i].ln;
                if(!vis[a[i].nx]){
                    vis[a[i].nx]=1;
                    q[++tl]=a[i].nx;
                }
            }
        }
    }return;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            char ch;cin>>ch;
            if(ch=='#'){c[i][j]=1;continue;}
            else if(ch=='C') S=get(i,j);
            else if(ch=='D') T=get(i,j);
        }
    }for(int i=2;i<n;++i){
        for(int j=1;j<=m;++j){
            if(!c[i][j]){
                if(!c[i+1][j]) add(get(i,j),get(i+1,j),0);
                if(!c[i-1][j]) add(get(i,j)+n*m,get(i-1,j)+n*m,0);
                if(c[i+1][j]){
                    if(j<m&&!c[i][j+1]) add(get(i,j),get(i,j+1),0);
                    if(j>1&&!c[i][j-1]) add(get(i,j),get(i,j-1),0);
                }if(c[i-1][j]){
                    if(j<m&&!c[i][j+1]) add(get(i,j)+n*m,get(i,j+1)+n*m,0);
                    if(j>1&&!c[i][j-1]) add(get(i,j)+n*m,get(i,j-1)+n*m,0);
                }
            }
        }
    }for(int i=1;i<=m;++i){
        if(!c[1][i]&&!c[2][i]) add(i,i+m,0);
        if(!c[n][i]&&!c[n-1][i])add(get(n,i)+n*m,get(n-1,i)+n*m,0);
    }for(int i=2;i<n;++i){
        for(int j=1;j<=m;++j){
            if(!c[i][j]){
                if(c[i+1][j]) add(get(i,j),get(i,j)+n*m,1);
                if(c[i-1][j]) add(get(i,j)+n*m,get(i,j),1);
            }
        }
    }SPFA(S);if(min(ln[T],ln[T+n*m])<2e9)
    printf("%d\n",min(ln[T],ln[T+n*m]));
    else puts("-1");
    return 0;
}