P3956 棋盘 题解

CSDN同步

原题链接

简要题意:

\(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;
}



posted @ 2020-04-04 21:18  bifanwen  阅读(198)  评论(0编辑  收藏  举报