NOIP2013华容道

MD调的我肾都要虚了!!!
先陈述一下我现在的激动心情……这道题昨天晚上调了一晚上没调出来今天晚上又调到了现在……而且这道题没有看别人的代码算是完全独立完成(毕竟我这种蒟蒻这样也不容易),中途还怀疑自己的思路对不对想看别人的代码验证自己思路但是真的看不进去啊!!(还好没看进去不然真是太cuo了)调了那么久总算过样例了,抱着“我先交一发看看能A几个点的心态”交题了,突然就全绿了吓得我flappybird都掉了!!!

解法

说起来很容易:
每个点拆成四种状态:空格在棋子(i,j)上/下/左/右,预处理(bfs)出每个点到其相邻点的距离(也就是白格需要移动的步数)。步骤是:白格到达指定相邻点的距离+起始棋子点到终止白格的位置 的距离
然后跑SPFA就可以了
最后要加上初始白格位置到初始棋子位置的距离(bfs)

注意&教训:

1.白格到达指定相邻点的距离+起始棋子点到终止白格的位置 的距离”:
1>白格到达指定相邻点的距离 这个过程不能经过起始棋子的位置
2>起始棋子点到终止白格的位置 这个过程不能经过终止棋子的位置
就是因为2>调了那么久!!不过自己也是太不注意了,随手两个-1就写上去了!!写题的时候不要嫌麻烦!!该动笔一定要动笔!!不然调题的时候更麻烦!!!
2.我的p()的写法(把各种状态表示成了整数)十分的不清晰!!!30*30的格子多开几维数组没关系的啊!!这也给后面调代码带来了不小的困难。
3.亏得我不是在考试的时候遇到这道题,不然就……GG了。所以一定要记牢前两条啊。

最后附上十分难看的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int M=35,N=35;
typedef pair<int,int> pii;
int n,m,q,a[N][M];
const int inf=1e9;
int x[]={0,1,0,-1},y[]={1,0,-1,0};
int px[]={0,-1},py[]={-1,0};
int head[N*N*4],nxt[N*N*10],to[N*N*10],w[N*N*10],etot;
void adde(int u,int v,int c)
{
    to[++etot]=v;
    nxt[etot]=head[u];
    w[etot]=c;
    head[u]=etot;
}
int p(int i,int j,int k)
{
    return ((i-1)*m+j-1)*4+k+1;
}
int bfs(pii st,pii ed,int nx,int ny)
{
    if(st==ed) return 0;
    bool vis[N][N];
    int d[N][N];
    memset(vis,0,sizeof(vis));
    queue<pii> q;
    q.push(st);
    d[st.first][st.second]=0;
    vis[st.first][st.second]=1;
    while(!q.empty()){
        pii u=q.front();q.pop();
        for(int i=0;i<4;i++){
            int xx=u.first+x[i],yy=u.second+y[i];
            if(xx<1||xx>n||yy<1||yy>m) continue;
            if(vis[xx][yy]||!a[xx][yy]||(xx==nx&&yy==ny)) continue;
            d[xx][yy]=d[u.first][u.second]+1;
            if(make_pair(xx,yy)==ed) return d[xx][yy];
            vis[xx][yy]=1;
            q.push(make_pair(xx,yy));
        }
    }
    return inf;
}
int SPFA(int st,pii ed,int qq)
{
    int dis[10*N*N];bool exi[10*N*N];
    queue<int> q;
    memset(dis,64,sizeof(dis));
    dis[st]=0;
    q.push(st);
    while(!q.empty()){
        int u=q.front();q.pop();
        exi[u]=0;
        for(int i=head[u];i;i=nxt[i]){
            int v=to[i];
            if(dis[v]>dis[u]+w[i]){
                dis[v]=dis[u]+w[i]; 
                if(!exi[v]){
                    exi[v]=1;
                    q.push(v);
                }
            }
        }
    }
    int ans=inf;
    for(int i=0;i<4;i++){
        int xx=dis[p(ed.first,ed.second,i)];
        ans=min(ans,xx);
    }
    return ans;
}
int main()
{
    memset(w,64,sizeof(w));
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
        scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
        if(!a[i][j]) continue;
        for(int k=0;k<4;k++){
            int sx=i+x[k],sy=j+y[k];//起始白格的方位 
            if(sx<1||sx>n||sy<1||sy>m||!a[sx][sy]) continue;
            for(int r=0;r<2;r++){
                int ex=i+px[r],ey=j+py[r];//终止点的方位 
                if(ex<1||ex>n||ey<1||ey>m||!a[ex][ey]) continue;
                for(int h=0;h<4;h++){
                    int exx=ex+x[h],eyy=ey+y[h];//终止白格的方位
                    if(exx<1||exx>n||eyy<1||eyy>m||!a[exx][eyy]) continue;
                    int dis=0;
                    int dis1=bfs(make_pair(sx,sy),make_pair(ex,ey),i,j);
                    //不能经过起始点 
                    //起始白格到终止点的距离 

                    if(dis1==inf) continue;
                    int dis2=bfs(make_pair(i,j),make_pair(exx,eyy),ex,ey);
                    //起始点到终止白格的位置 !!!
                    //能否经过终止点???不能!!
                    //能否经过起始白格?可以啊?? 
                    if(dis2==inf) continue;
                    dis=dis1+dis2+1;
                    adde(p(i,j,k),p(ex,ey,h),dis);
                    adde(p(ex,ey,h),p(i,j,k),dis);
                }
            }   
        }
    }
    while(q--){
        int EX,EY,SX,SY,TX,TY;
        //空白的格子在第 EX_i 行第 EY_i 列,
        //指定的可移动棋子的初始位置为第 SX_i 行第 SY_i 列
        //目标位置为第 TX_i 行第 TY_i 列。
        scanf("%d%d%d%d%d%d",&EX,&EY,&SX,&SY,&TX,&TY);
        if(!a[SX][SY]||!a[EX][EY]||!a[TX][TY]){printf("-1\n");continue;} 
        if(SX==TX&&SY==TY) {printf("0\n");continue;}
        int dis[5];
        memset(dis,64,sizeof(dis));
        for(int i=0;i<4;i++){
            int xx=x[i]+SX,yy=y[i]+SY;
            if(xx<1||xx>n||yy<1||yy>m||!a[xx][yy]) continue;
            dis[i]=bfs(make_pair(EX,EY),make_pair(xx,yy),SX,SY);
            //从白格的原始位置到达指定棋子旁边

        }
        int ans=inf;
        for(int i=0;i<4;i++){
            int xx=x[i]+SX,yy=y[i]+SY;
            if(xx<1||xx>n||yy<1||yy>m||!a[xx][yy]) continue;
            int x=SPFA(p(SX,SY,i),make_pair(TX,TY),q)+dis[i];
            ans=min(ans,x);
        }
        if(ans==inf) printf("-1\n");
        else printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值