TOJ 5993 棋盘 优先队列或者DFS

5993: 棋盘 分享至QQ空间
时间限制(普通/Java):1000MS/3000MS 内存限制:65536KByte
总提交: 22 测试通过:9
描述

有一个m×m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。

任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的), 你只能向上、 下、左、 右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 1个金币。

另外, 你可以花费 2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用, 而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法; 只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。

现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?

输入

第一行包含两个正整数m,n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。

接下来的nn行,每行三个正整数x,y,c, 分别表示坐标为(x,y)的格子有颜色c。

其中c=1 代表黄色,c=0 代表红色。 相邻两个数之间用一个空格隔开。 棋盘左上角的坐标为(1,1),右下角的坐标为(m,m)。

棋盘上其余的格子都是无色。保证棋盘的左上角,也就是(1,1) 一定是有颜色的。

输出

一个整数,表示花费的金币的最小值,如果无法到达,输出−1。

样例输入

5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0

样例输出

8

提示

样例输入2

5 5
1 1 0
1 2 0
2 2 1
3 3 1
5 5 0

样例输出2
-1

输入输出样例 1 说明
在这里插入图片描述

从(1,1)开始,走到(1,2)不花费金币

从(1,2)向下走到(2,2)花费 1 枚金币

从(2,2)施展魔法,将(2,3)变为黄色,花费 2 枚金币

从(2,2)走到(2,3)不花费金币

从(2,3)走到(3,3)不花费金币

从(3,3)走到(3,4)花费 1枚金币

从(3,4)走到(4,4)花费 1 枚金币

从(4,4)施展魔法,将(4,5)变为黄色,花费2 枚金币,

从(4,4)走到(4,5)不花费金币

从(4,5)走到(5,5)花费 1 枚金币

共花费 8枚金币。

输入输出样例 2 说明
在这里插入图片描述

从(1,1)走到(1,2),不花费金币

从(1,2)走到(2,2),花费1金币

施展魔法将(2,3)变为黄色,并从(2,2)走到(2,3)花费2 金币

从(2,3)走到(3,3)不花费金币

从(3,3)只能施展魔法到达(3,2),(2,3),(3,4),(4,3)

而从以上四点均无法到达(5,5),故无法到达终点,输出−1

数据规模与约定

对于30%的数据, 1≤m≤5,1≤n≤10。

对于60%的数据, 1≤m≤20,1≤n≤200。

对于100%的数据, 1≤m≤100,1≤n≤1,000。

题解:这题我一拿到就想到用DFS深搜回溯,或者是优先队列来写,先尝试了一下DFS没剪枝跑了55分,后面对最终答案剪枝多跑了一个点,看了一下别人的剪枝,对每个点都要做最优解剪枝就可以跑过,其实思想和优先队列差不多。
优先队列来写就很简单,只要保证每次加进去的点的时间要小就行了。
其实这题一开始还思考过,变色的那个点是否要和当前点颜色相同,如果变不同色会不会有更优的答案,其实是一样的,有可能导致答案不同的情况是下下个点的颜色,如果不同色的话,比如该点是黄色,下下个点是红色,你变下个点位黄色,消耗2+1的时间,变红色消耗3+0的时间,效果是一样的。如果同色的话,那变不同色还需要多花2的时间,所有变同色是能保证最优解的。
下面贴两种代码

优先队列 BFS

#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
struct node{
    int x,y,flag,t;
    bool friend operator < (node a,node b){ // 耗时小的先进队
        return a.t > b.t;
    }
};
int n,m,a[maxn][maxn];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
bool vis[maxn][maxn];
void bfs(){
    priority_queue<node>q;
    node now,next;
    now.x=now.y=now.flag=1,now.t=0;vis[1][1]=1;
    q.push(now);
    while(!q.empty()){
        now = q.top();q.pop();
        if(now.x==n&&now.y==n){ //答案找到
            printf("%d\n",now.t);
            return;
        }
        for(int i=0;i<4;i++){
            int nx = now.x + dir[i][0];
            int ny = now.y + dir[i][1];
            if(nx>=1&&ny>=1&&nx<=n&&ny<=n&&!vis[nx][ny]){//合法点
                if(a[nx][ny]){ //如果是有颜色的点
                    next.x=nx,next.y=ny,next.flag=1;vis[nx][ny]=1;
                    if(a[nx][ny]==a[now.x][now.y]) next.t=now.t; //点相同时间一样
                    else next.t=now.t+1; //不同则加一
                    q.push(next);
                }
                else{ //无色点
                    if(now.flag){ //上一次不是变色来的,表示这次可以变色 
                        next.x=nx,next.y=ny,next.flag=0,next.t=now.t+2;
                        vis[nx][ny]=1;a[nx][ny]=a[now.x][now.y];
                        q.push(next);
                    }
                }
            }
        }
    }
    printf("-1\n");
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        a[x][y]=z+1;
    }
    bfs();
}

DFS 深搜回溯

#include<bits/stdc++.h>
using namespace std;
const int maxn = 105,inf = 0x3f3f3f3f;
int a[maxn][maxn],n,m;
int dp[maxn][maxn]; //保存每个点的最优解
int dir[4][2]={1,0,0,1,-1,0,0,-1};
void dfs(int x,int y,int ans,int flag){ //flag 用来记录是否能变色
    if(ans>=dp[x][y]) return; //大于最优解不合法
    dp[x][y]=ans;
    for(int i=0;i<4;i++){
        int nx = x + dir[i][0];
        int ny = y + dir[i][1];
        if(nx>=1&&ny>=1&&nx<=n&&ny<=n){ //合法点
            if(!a[nx][ny]){ //无色
                if(flag){ //如果可以变色
                    a[nx][ny]=a[x][y]; //变色
                    dfs(nx,ny,ans+2,0); 
                    a[nx][ny]=0; //回溯
                }
            }
            else{
                if(a[nx][ny]==a[x][y]) dfs(nx,ny,ans,1);
                else dfs(nx,ny,ans+1,1);
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    memset(dp,inf,sizeof dp);
    for(int i=0;i<m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        a[x][y]=z+1;
    }
    dfs(1,1,0,1);
    printf("%d", dp[n][n]==inf?-1:dp[n][n]);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值