NOIP2017普及组 棋盘

Luogu P3956 棋盘

题目链接

考试的时候拿到这一题也是心情复杂,因为这种框框地图题平常也是有打的,以为会很容易,但是又想到这种题Debug很累,所以心情复杂。

但看完题我就觉得自己真天真啊……这哪是难Debug,是难打啊……但是当时并不知道怎么做,只能按之前打了一个DFS暴力搜索+一个可行性剪枝,最后成绩出来骗了50分。

考完试在Yugu群上看见kkk说这题正解是建图跑最短路,还有一些人说是记忆化搜索。因为记忆化搜索打起来方便呐,在车上就打了记忆化,但是!!这题存在后效性!!我会的记忆化是做不了这个了(弱啊我),当然好像的确是有记忆化这种做法的。

今天的解法就是跑一遍最短路。

    include <cstdio>
    using namespace std;
    int dis[105][105],vis[105][105],map[105][105],clo[105][105];
    int x[1000005],y[1000005];
    const int INF=10000000;//因为权值无论如何都不会大于它,所以用它来表示无法到达(没有被更新到就是这个值)
    const int SX[]={1,0,-1,0};
    const int SY[]={0,-1,0,1};
    int main(){
        int n,m;
        scanf("%d %d",&m,&n);//m*m的地图,n个点有color
        for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++){
            dis[i][j]=INF;//表示[i][j]到[1][1]的distance
            vis[i][j]=0;//标记此节点是否位于queue
            map[i][j]=0;//记录此节点color
            clo[i][j]=0;//记录此节点是否magic
        }
        //初始化
        int xx,yy,s;//[xx][yy]的颜色为(s+1):因为0标记为无色,而题目的0是红色,那为了方便则+1处理
        for(int i=1;i<=n;i++){
            scanf("%d %d %d",&xx,&yy,&s);
            map[xx][yy]=s+1;
        }
        dis[1][1]=0;
        vis[1][1]=1;
        x[1]=1;
        y[1]=1;//SPFA的队列x和y分别保存横纵坐标
        int head=1,tail=1,cnt=0;
        while(head<=tail){
            cnt=0;
            for(int i=head;i<=tail;i++){//访问队列中节点
                int u=x[i],v=y[i];
                int nx,ny;
                int book=map[u][v];//存自己的color
                if(clo[u][v])book=clo[u][v];
                for(int j=0;j<4;j++){//尝试四个方向
                    nx=u+SX[j];ny=v+SY[j];
                    if(!(nx>=1&&nx<=m&&ny>=1&&ny<=m))continue;
                      //如果越界则跳过
                    int w,fclo;//表示处理后需要放入队列的数据
                    if(book==map[nx][ny]){
                        w=dis[u][v];
                        fclo=0;
                    }
                    //如果颜色一致,则距离代价为0,不修改颜色,没有magic
                    else if(book!=map[nx][ny]&&map[nx][ny]!=0){
                        w=dis[u][v]+1;
                        fclo=0;
                    }
                    //如果颜色不一致但是又不需要magic,则距离+1其余不变
                    else if(map[nx][ny]==0&&clo[u][v]==0){
                        w=dis[u][v]+2;
                        fclo=book;
                    }
                    //如果颜色为空,但是之前无magic,则可以距离+2,颜色变成自己,magic标记clo为1
                    else continue;//啥都不满足就说明这节点没法到
                    if(w<dis[nx][ny]){//如果使得路径更优,那可以进行松弛操作
                        dis[nx][ny]=w;
                        clo[nx][ny]=fclo;
                        if(vis[nx][ny])continue;//如果在队列里是不需要管他了,后面会处理到的
                          //否则我们就需要把他入队
                        cnt++;//新入队数++
                        x[cnt+tail]=nx;
                        y[cnt+tail]=ny;//放在队尾
                        vis[nx][ny]=1;
                    }
                }
                vis[u][v]=0;
                if(clo[u][v]){
                    map[u][v]=0;
                    clo[u][v]=0;
                }
                //如果此节点上次magic了,则需要复原
            }
            head=tail+1;
            tail+=cnt;
        }
        if(dis[m][m]==INF)printf("%d\n",-1);
        else printf("%d\n",dis[m][m]);
        return 0;
    }

我们可以把此图看成一个无向图。图上某坐标[i][j]可以认为是某节点[i][j],并且有四条路径到它的上下左右的节点,这样,我们就可以尝试着按最短路去做做了。

Dijkstra和SPFA应该都可以做,但是我个人用SPFA(比较像BFS好打)。

总结一下思想:

采用SPFA进行最短路操作,对队列中的某个节点进行松弛操作(广告:感谢浴谷教育教会我最短路的一系列玩法),若路径另一端的节点被松弛操作,我们就需要更新一下它的信息:dis(到[1][1]的距离),clo(是否用了膜法,用了膜法变成什么颜色),此节点若在队列中,则后续会操作它,若不在,则需要入队,以他为起点进行松弛操作。最后得出到点[m][m]的最短距离即为答案。

转载于:https://www.cnblogs.com/Neworld2002/p/8470901.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值