题目描述
有一个m×m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。
任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的), 你只能向上、 下、左、 右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 1个金币。
另外, 你可以花费 2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用, 而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法; 只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。
现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?
题目分析
深度优先搜索+剪枝(合理化地去掉不符合情况的),深搜的核心是去寻找递归的参数与 结束的条件!深搜第一步肯定是从(1,1)坐标开始,然后向四周扩散,因为理论上允许多次踏入同一块格子,所以不设visit变量。同时考虑 魔法 这一附加条件,魔法不能连续施展,默认第一次允许使用魔法,且第一块格子一定有色。
所以对于下一个格子来说,有两种情况:
1.下一个格子有色,则考虑是否与上一个格子同色或异色;
2.下一个格子无色,则考虑本次能否使用魔法,能使用魔法,则继续搜,同时再下一次不能使用魔法;如果本次不能使用魔法,则不能跳到下一个格子上,即没有操作。
深搜中剪枝的情况:
1.出界,棋盘是一个m*m的坐标系;
2.深搜的参数 sum大于此时这个格子的最小花费cost[x][y]时;
AC代码
#include<iostream>
#include<cmath>
#include<stdio.h>
#include<stdlib.h>
#include<cstdio>
#include<time.h>
#include<cmath>
#include<algorithm>
using namespace std;
int inf=0x7fffffff;
int cost[105][105]; //每一个点的最小金币花费
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1}; //某一个点向四周运动
int map[105][105];//表示棋盘坐标,数值用于储存颜色值,0代表无色,1代表红色,2代表黄色
int m;
void dfs(int x,int y,int sum,int flag)
{ //深搜,剪枝,递归 flag=1表示本次可以使用魔法,flag=0表示上一次已经使用了魔法,本次无法使用
if(x<1||x>m||y<1||y>m) return ;//已经出界
if(sum>=cost[x][y]) return ; //此时的金币花费已经大于已经求得的最小值,出界!
else cost[x][y] = sum;
for(int i=0;i<4;i++) //向 上下左右 四个点去搜索
{
int xx=x+dx[i];
int yy=y+dy[i];
if(map[xx][yy]>0) //下一个点有色
{
if(map[xx][yy]==map[x][y])
dfs(xx,yy,sum,1);
else //颜色不同,花费一个金币
dfs(xx,yy,sum+1,1);
}
else //若下一个点无色
if(flag==1) // 且本次可以使用魔法
{
map[xx][yy]=map[x][y];
dfs(xx,yy,sum+2,0);//下一次不能使用魔法
map[xx][yy]=0;//回溯
}
}
}
int main()
{
int n;
cin>>m>>n;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
cost[i][j]=inf; //初始化花费值为inf
for(int i=1;i<=n;i++)
{
int x,y,c;
cin>>x>>y>>c;
map[x][y]=c+1; //map默认为0
}
dfs(1,1,0,1);//深搜结束,可以求得从(1,1)点到达任意一个点的最小金币花费
if(cost[m][m] != inf) cout<<cost[m][m];
else cout<<-1;
}