题目大意:
有一个 n×n n × n 的棋盘,有2种颜色。每次你可以移到上下左右中的一格,如果这两个各自颜色相同,那么就不用花钱,如果颜色不同,将要花一块钱。这个棋盘上还有一些格子是没有颜色的,你不能站在没颜色的格子上,但是你可以花两块钱用魔法将这个格子变为任意一个颜色,但是这个魔法不能连续使用,而你离开使用了魔法的格子之后,这个格子又将变回去。求从 (1,1) ( 1 , 1 ) 走到 (n,n) ( n , n ) 的最小花费。
思路:
DFS
D
F
S
原来可以过。。。
对于一个点
(x,y)
(
x
,
y
)
,我们设它的最小花费为
ans[x][y]
a
n
s
[
x
]
[
y
]
,那么最终答案就是
ans[n][n]
a
n
s
[
n
]
[
n
]
。
那么就可以打一个五维的
DFS
D
F
S
,
dfs(x,y,use,k,c)
d
f
s
(
x
,
y
,
u
s
e
,
k
,
c
)
分别表示这个点的坐标为
x
x
行列,上一次是否使用了魔法,走到这的花费和上一个格子的颜色。
那么又要分4种情况讨论:
- 如果这个格子没有颜色,并且上一次使用了魔法,那么就退出(不能连续两次使用魔法)。
- 如果这个格子没有颜色,但是上一次没有使用魔法,那么就使用一次魔法,将这个格子变成与上一个格子一样的颜色(为了保证答案最优)。
- 如果这个格子有颜色,并且和上一个格子的颜色一样,那么就不用花费走到这个格子上。
- 如果这个格子有颜色,但是与上一个格子颜色不一样,那么就花一块钱走到这个格子上。
伪代码如下:
if ((!a[xx][yy])&&use) continue;
if ((!a[xx][yy])&&(!use)) dfs(xx,yy,1,k+2,c);
if (a[xx][yy]&&a[xx][yy]==c) dfs(xx,yy,0,k,c);
if (a[xx][yy]&&a[xx][yy]!=c) dfs(xx,yy,0,k+1,a[xx][yy]);
那么怎么处理魔法呢?
每次使用完魔法之后,花费要加2,因为为了保证答案最优,肯定是要将下一个格子的颜色变成这个各自的颜色,所以使用魔法需要两块钱,但是由于颜色一样就不用再多加一块钱。
值得注意的是,使用魔法之后并不需要真正的在数组里赋值,而是假装它是有颜色的,
c
c
的值不变。如果将数组也改变了的话,程序将会很麻烦。
最终输出
ans[n][n]
a
n
s
[
n
]
[
n
]
即可。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int dx[]={0,0,0,-1,1};
const int dy[]={0,-1,1,0,0};
int n,m,a[201][201],xxx,yyy,color,ans[201][201];
void dfs(int x,int y,int use,int k,int c)
{
if (ans[x][y]<=k) return; //剪枝,如果之前到达这里的最小花费比现在到达这里的花费要小就不继续搜
ans[x][y]=k; //记录最小花费
for (int i=1;i<=4;i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if ((!a[xx][yy])&&use) continue;
if ((!a[xx][yy])&&(!use)) dfs(xx,yy,1,k+2,c);
if (a[xx][yy]&&a[xx][yy]==c) dfs(xx,yy,0,k,c);
if (a[xx][yy]&&a[xx][yy]!=c) dfs(xx,yy,0,k+1,a[xx][yy]);
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&xxx,&yyy,&color);
a[xxx][yyy]=color+1;
}
memset(ans,0x7f,sizeof(ans));
dfs(1,1,0,0,a[1][1]);
if (ans[n][n]<=1e9) printf("%d\n",ans[n][n]);
else printf("-1\n");
return 0;
}