P3956 棋盘 题解
简要题意:
从 \(1,1\) 走到 \(n,n\);每次走过相同颜色的格子不用魔法,走过不同颜色(但有颜色)的格子用 \(1\) 个魔法;走到一个有颜色的格子上可以暂时将它变成某种颜色然后走上去,用 \(2\) 个魔法;但是这种魔法是暂时的,只要你离开那个格子,那个格子就会变回没有颜色的样子,并且不能连续使用 \(2\) 次。求到终点的最小魔法。(不好意思,题目中实际说的是“金币”,但理解成魔法无妨。)
首先,\(\texttt{dfs}\) 和 \(\texttt{bfs}\) 都可以实现本题。
由于 \(\texttt{dfs}\) 代码原理简单,清晰有条理,所以考虑 深度优先搜索。
-
函数参数:当前坐标(两个数),使用金币个数,是否能用魔法。
-
状态转移:利用坐标转移公式,按照题意模拟。
-
剪枝优化:记忆化当前坐标答案。
具体细节见代码。
时间复杂度:\(O(n^2)\).(有较大常数,但可以接受)
实际得分;\(100pts\).
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
inline int read(){char ch=getchar();int f=1; while(!isdigit(ch)) {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f;}
int f[1001][1001],ans;
int n,m,a[1001][1001];
bool h[1001][1001];
const int dx[4]={0,0,1,-1};
const int dy[4]={1,-1,0,0}; //坐标转移
inline void dfs(int x,int y,int coin,bool magic) {
// printf("%d %d %d %d\n",x,y,coin,magic);
if(f[x][y]<=coin || coin>=ans) return; //即当前答案已经变劣
f[x][y]=coin;
if(x==n && y==n) {
ans=min(ans,coin);
return;
} for(int i=0;i<4;i++) {
int nx=x+dx[i],ny=y+dy[i];
if(nx<1 || ny<1 || nx>n || ny>n) continue;
if(!h[nx][ny]) {
h[nx][ny]=1;
if(a[nx][ny]) { //有颜色
if(a[x][y]==a[nx][ny]) dfs(nx,ny,coin,1);
else dfs(nx,ny,coin+1,1); //看用不用魔法
} else if(magic) { //可以用
a[nx][ny]=a[x][y];
dfs(nx,ny,coin+2,0); //下步不能用
a[nx][ny]=0;
} h[nx][ny]=0; //返回状态防止影响下一步答案
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=INT_MAX;
while(m--) {
int x=read(),y=read(),z=read();
a[x][y]=z+1; // 0 表示无色,所以 +1
} ans=INT_MAX; h[1][1]=1; dfs(1,1,0,1);
printf("%d\n",ans==INT_MAX?-1:ans);
return 0;
}
简易的代码胜过复杂的说教。