二解NOIP普及组2017年T3棋盘

题目描述:https://www.acwing.com/problem/content/473/
题目具体内容:
  有一个m×m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。 任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的),你只能向上、下、左、右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费1个金币。 另外,你可以花费2个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用,而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法;只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。 
现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?
【输入格式】
  数据的第一行包含两个正整数m,n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。  
  接下来的n行,每行三个正整数x,y,c,分别表示坐标为(x,y)的格子有颜色c,其中c=1代表黄色,c=0代表红色。
相邻两个数之间用一个空格隔开。棋盘左上角的坐标为(1, 1),右下角的坐标为(m, m)。  棋盘上其余的格子都是无色,保证棋盘的左上角,也就是(1,1)一定是有颜色的。
【输出格式】
  输出一行,一个整数,表示花费的金币的最小值,如果无法到达,输出-1。
【数据范围】
  1≤m≤100,
  1≤n≤1000
【输入样例】:
  5 7
  1 1 0
  1 2 0
  2 2 1
  3 3 1
  3 4 0
  4 4 1
  5 5 0
【输出样例】:
  8
分析:这个题目题面比较好理解,在训练的基本平面格子上的搜索上做了一些变型,除了成上下左右走之外,还需要根据相邻格子间的颜色来决定是否能走,如果能走,走下去将会产生的金币数要维护最小值。求从格子左上到右下的最少花费金币数。
搜索可以采用宽搜和深搜,在搜索时记录当前所产生的金币数。
具体代码如下:
【宽搜BFS写法】

#include<bits/stdc++.h>
using namespace std;

struct sts{
	int x,y,nowcolor,step;
};
int chess[100+6][100+6]; //存储从左上走到当前格子上时的最少金币数 
int chcol[106][106];  //存储格子上的颜色值,-1代表透明,0代表红色,1代表黄色 
queue<sts>q;    //宽搜需要用到的队列STL 
const int dx[5]={0,1,0,-1,0},dy[5]={0,0,1,0,-1}; 
int main(){
	int m,n;
	scanf("%d%d",&m,&n);
	memset(chess,0x3f,sizeof(chess));
	memset(chcol,-1,sizeof(chcol));
	for (int i = 1;i <= n; i++){
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		chcol[x][y] = c;   //0代表红色,1代表黄色
	}
	q.push((sts){1,1,chcol[1][1],0});
	chess[1][1] =0;
	while(!q.empty()){
		sts t = q.front();
		q.pop();		
		for(int i = 1 ;i<= 4; i++){
			int newx = t.x + dx[i];
			int newy = t.y + dy[i];
			int nowcolor = chcol[newx][newy];
			if(newx < 1 || newx > m || newy < 1 || newy > m) continue;	
			//计算金币数 
			int money = 0;	
			if(chcol[t.x][t.y] == -1){
				if(chcol[newx][newy] == -1) money = -1;
				else if(t.nowcolor == chcol[newx][newy]) money = 0;
				else money = 1;
			}
			else{
				if(chcol[newx][newy] == -1) money = 2,nowcolor = chcol[t.x][t.y];
				else if(chcol[t.x][t.y] == chcol[newx][newy]) money = 0;
				else money = 1;
			}
			//如果能走,在走下来的金币数更少时更新走到该格子的最少金币数,并入队列。 
			if(money != -1 && chess[newx][newy]> t.step+ money){
				chess[newx][newy] = t.step + money;
				q.push((sts){newx,newy,nowcolor,chess[newx][newy]});			
			}
		}
	}
	if(chess[m][m] == 0x3f3f3f3f) chess[m][m] = -1;
	printf("%d",chess[m][m]);
	return 0;
} 

【深搜DFS写法】

#include<bits/stdc++.h>
using namespace std;

int chess[100+6][100+6];
int chcol[106][106];
int is_ov[105][105];
int ans = 0x3f3f3f3f;
int m,n;
const int dx[5]={0,1,0,-1,0},dy[5]={0,0,1,0,-1}; 

void dfs(int x,int y,int step,int lastcol){
	if(chess[x][y] <= step) return;   //记忆化 
	chess[x][y] = step;
	for(int i = 1;i<= 4;i++){
		int newx = x + dx[i], newy = y + dy[i];
		if(newx < 1 || newx > m || newy < 1 || newy > m) continue;	
		//计算金币数。 
		int money = 0;	
		if(chcol[x][y] == -1){
			if(chcol[newx][newy] == -1) money = -1;
			else if(lastcol == chcol[newx][newy]) money = 0;
			else money = 1;
		}
		else{
			if(chcol[newx][newy] == -1) money = 2;
			else if(chcol[x][y] == chcol[newx][newy]) money = 0;
			else money = 1;
		}
		//能继续往下走的,则继续搜索。 
		if(money != -1 ) {	
		    if(money == 2)	dfs(newx,newy,step+money, chcol[x][y]); 
		    else dfs(newx,newy,step+money, chcol[newx][newy]); 
		}
	}
}
int main(){
	
	scanf("%d%d",&m,&n);
	memset(chess,0x3f,sizeof(chess));
	memset(chcol,-1,sizeof(chcol));
	for (int i = 1;i <= n; i++){
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		chcol[x][y] = c;   //0代表红色,1代表黄色
	}
	dfs(1,1,0,0); 
	if(chess[m][m] == 0x3f3f3f3f) chess[m][m] = -1;
	printf("%d",chess[m][m]);
	return 0;
} 
上一篇:随机化算法


下一篇:1162. As Far from Land as Possible