算法题:打地鼠 图的路径查找

新鲜热乎的某厂笔试最后一题。

题目描述

有一个 n * m 个格子的矩阵mp时不时会冒出一些地鼠。mp[i][j] 表示每过 mp[i][j] 的时间第 i 行第 j 列的格子会冒出一只地鼠。
初始时,时间为0,你在第1行第1列格子处,每秒你必须向上下左右四个方向移动一格,不能走出矩阵外。
作为一个合格的打地鼠玩家,如果这一秒你从格子 a 走到格子 b ,下一秒你不能从格子 b 走到格子 a 。如果你走到一个格子的时候,这里刚好冒出一只地鼠,你就可以打一下地鼠。
你的终点是第n行第m列的格子,给你 t 秒时间,问你最多打几下地鼠。
最后你必须到达终点,如果第t秒时你不在终点,算作打了0次地鼠。

输入描述

第一行三个整数n,m,t
接下来n行,每行m个整数表示矩阵mp[i][j]。
2 <= n,m <= 10,1 <= t,mp[i][j] <= 1000

输出描述

输出一行一个整数表示答案

示例:

输入

2 2 6
1 1
1 1

输出

0

说明

一种行走方案是 (1, 1) -> (1, 2) -> (2, 2) -> (2, 1) -> (1, 1) -> (1, 2) -> (2, 2) ,每步都可以打一下地鼠。

解题思路

此题本质上是一个四维动态规划问题,时间、方向、地图横坐标和纵坐标各为一维。个人认为因为移动的范围受到时间限制,如果直接暴力遍历四维数组有过多冗余计算,也不方便剪枝优化。因为题意核心是最优路径查找,只是不用输出各个路径节点,所以可以运用类似各种寻路算法的思想,采用图的广度优先遍历的方法,通过维护更新一个玩家状态的队列的方式来求得结果。

C++实现代码

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

enum Direction
{
	eEast,
	eWest,
	eNorth,
	eSouth,
	eStart
};

struct State 
{
	int t;
	int i;
	int j;
	Direction from;
	int score;
	State(int t_, int i_, int j_, Direction from_, int score_) :t(t_), i(i_), j(j_), from(from_), score(score_){}
};

void WhacAMole()
{
	int n, m, t;
	cin >> n >> m >> t;

	vector<vector<int>> mp(n, (vector<int>)(m));
	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> mp[i][j];
		}
	}

	int ans = 0;

	queue<State> q;
	//根据题意每次必须移动一格且不能原路返回,所以默认mp大小至少是2 x 2
	//且默认起点在(0,0),所以先把(0,1),(1,0)入队
	q.push(State(1, 1, 0, eNorth, (1 % mp[1][0] == 0)));
	q.push(State(1, 0, 1, eEast, (1 % mp[0][1] == 0)));
	while (!q.empty())
	{
		State cur = q.front();
		q.pop();
		
		if (cur.i == n - 1 && cur.j == m - 1 && cur.t == t)	//默认终点位置是(n-1, m -1), 且必须t时刻正好走到终点才可以
		{
			ans = max(ans, cur.score);
		}
		
		int t_new = cur.t + 1;
		if (t_new > t)
			continue;

		if (cur.i > 0 && cur.from != eNorth)	//是否可向上走
		{
			int i_new = cur.i - 1;
			int j_new = cur.j;
			if (n - 1 - i_new + m - 1 - j_new <= t - cur.t)	//如果当前位置在剩余时间内到达不了终点则略过	
			{
				q.push(State(t_new, i_new, j_new, eSouth, cur.score + (t_new % mp[i_new][j_new] == 0))); //t_new % mp[i_new][j_new]为0说明代表当前位置计时已到
			}
		}

		if (cur.i < n - 1 && cur.from != eSouth)
		{
			int i_new = cur.i + 1;
			int j_new = cur.j;
			if (n - 1 - i_new + m - 1 - j_new <= t - cur.t)
			{
				q.push(State(t_new, i_new, j_new, eNorth, cur.score + (t_new % mp[i_new][j_new] == 0)));
			}
		}

		if (cur.j > 0 && cur.from != eEast)
		{
			int i_new = cur.i;
			int j_new = cur.j - 1;
			if (n - 1 - i_new + m - 1 - j_new <= t - cur.t)
			{
				q.push(State(t_new, i_new, j_new, eWest, cur.score + (t_new % mp[i_new][j_new] == 0)));
			}
		}

		if (cur.j < m -1 && cur.from != eWest)
		{
			int i_new = cur.i;
			int j_new = cur.j + 1;
			if (n - 1 - i_new + m - 1 - j_new <= t - cur.t)
			{
				q.push(State(t_new, i_new, j_new, eEast, cur.score + (t_new % mp[i_new][j_new] == 0)));
			}
		}
	}
	
	cout << ans << endl;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值