Tag Archives:

LeetCode Frog Position After T Seconds

1377. Frog Position After T Seconds

Given an undirected tree consisting of n vertices numbered from 1 to n. A frog starts jumping from the vertex 1. In one second, the frog jumps from its current vertex to another unvisited vertex if they are directly connected. The frog can not jump back to a visited vertex. In case the frog can jump to several vertices it jumps randomly to one of them with the same probability, otherwise, when the frog can not jump to any unvisited vertex it jumps forever on the same vertex. 

The edges of the undirected tree are given in the array edges, where edges[i] = [fromi, toi] means that exists an edge connecting directly the vertices fromi and toi.

Return the probability that after t seconds the frog is on the vertex target.

Example 1:

Input: n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 2, target = 4
Output: 0.16666666666666666 
Explanation: The figure above shows the given graph. The frog starts at vertex 1, jumping with 1/3 probability to the vertex 2 after second 1 and then jumping with 1/2 probability to vertex 4 after second 2. Thus the probability for the frog is on the vertex 4 after 2 seconds is 1/3 * 1/2 = 1/6 = 0.16666666666666666. 

Example 2:

Input: n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 1, target = 7
Output: 0.3333333333333333
Explanation: The figure above shows the given graph. The frog starts at vertex 1, jumping with 1/3 = 0.3333333333333333 probability to the vertex 7 after second 1. 

Example 3:

Input: n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 20, target = 6
Output: 0.16666666666666666

Constraints:

  • 1 <= n <= 100
  • edges.length == n-1
  • edges[i].length == 2
  • 1 <= edges[i][0], edges[i][1] <= n
  • 1 <= t <= 50
  • 1 <= target <= n
  • Answers within 10^-5 of the actual value will be accepted as correct.

这题其实不是很hard。

给定一个无向树,节点编号是1~n。有一个青蛙开始在1号节点,每秒钟它会随机跳到与其相邻的未被访问的过的节点中,注意不能走回头路,访问过的节点不能再访问了。如果它周围的节点都被访问了,则它就在原地打转。问经过t秒,它跳到指定节点的概率是多少。

解法。从1号节点开始BFS,每到一个节点时,记录当前走过的时间以及走到当前节点的概率。计算概率也很简单,维护一个visited数组,看看有多少个未被访问的邻居child,则路径概率要乘上(1/child)。

还需要注意的是,如果走到目标节点的时间大于给定时间,则走不到目标节点;如果走的时间小于给定时间,则要看看目标节点还有没有未被访问的节点,如果没有的话,青蛙在目标节点原地打转,否则青蛙就跳走了不会再回来了。

完整代码如下:

struct Point {
	int id, time;
	double prob;
	Point(int i, int t, double p) :id(i), time(t), prob(p) {};
};

class Solution {
public:
	double frogPosition(int n, vector<vector<int>>& edges, int t, int target) {
		vector<vector<int>> graph(n + 1, vector<int>(n + 1, 0));
		for (int i = 0; i < edges.size(); ++i) {
			graph[edges[i][0]][edges[i][1]] = 1;
			graph[edges[i][1]][edges[i][0]] = 1;
		}
		vector<int> visited(n + 1, 0);
		double prob = 1.0;
		int time = 0;

		queue<Point> q;
		q.push(Point(1, 0, 1.0));
		while (true) {
			Point cur = q.front();
			q.pop();
			if (cur.id == target) {
				prob = cur.prob;
				time = cur.time;
				break;
			}
			visited[cur.id] = 1;
			int child = 0;
			for (int i = 1; i <= n; ++i) {
				if (visited[i] == 0 && graph[cur.id][i] == 1) {
					++child;
				}
			}
			for (int i = 1; i <= n; ++i) {
				if (visited[i] == 0 && graph[cur.id][i] == 1) {
					Point next(i, cur.time + 1, cur.prob / child);
					q.push(next);
				}
			}
		}

		if (t < time)return 0;
		else if (t == time)return prob;
		else {
			int child = 0;
			for (int i = 1; i <= n; ++i) {
				if (visited[i] == 0 && graph[target][i] == 1) {
					++child;
				}
			}
			if (child == 0)return prob;
			else return 0;
		}

	}
};

本代码提交AC,用时40MS。

LeetCode Minimum Cost to Make at Least One Valid Path in a Grid

1368. Minimum Cost to Make at Least One Valid Path in a Grid

Given a m x ngrid. Each cell of the grid has a sign pointing to the next cell you should visit if you are currently in this cell. The sign of grid[i][j] can be:

  • 1 which means go to the cell to the right. (i.e go from grid[i][j] to grid[i][j + 1])
  • 2 which means go to the cell to the left. (i.e go from grid[i][j] to grid[i][j - 1])
  • 3 which means go to the lower cell. (i.e go from grid[i][j] to grid[i + 1][j])
  • 4 which means go to the upper cell. (i.e go from grid[i][j] to grid[i - 1][j])

Notice that there could be some invalid signs on the cells of the grid which points outside the grid.

You will initially start at the upper left cell (0,0). A valid path in the grid is a path which starts from the upper left cell (0,0) and ends at the bottom-right cell (m - 1, n - 1) following the signs on the grid. The valid path doesn’t have to be the shortest.

You can modify the sign on a cell with cost = 1. You can modify the sign on a cell one time only.

Return the minimum cost to make the grid have at least one valid path.

Example 1:

Input: grid = [[1,1,1,1],[2,2,2,2],[1,1,1,1],[2,2,2,2]]
Output: 3
Explanation: You will start at point (0, 0).
The path to (3, 3) is as follows. (0, 0) --> (0, 1) --> (0, 2) --> (0, 3) change the arrow to down with cost = 1 --> (1, 3) --> (1, 2) --> (1, 1) --> (1, 0) change the arrow to down with cost = 1 --> (2, 0) --> (2, 1) --> (2, 2) --> (2, 3) change the arrow to down with cost = 1 --> (3, 3)
The total cost = 3.

Example 2:

Input: grid = [[1,1,3],[3,2,2],[1,1,4]]
Output: 0
Explanation: You can follow the path from (0, 0) to (2, 2).

Example 3:

Input: grid = [[1,2],[4,3]]
Output: 1

Example 4:

Input: grid = [[2,2,2],[2,2,2]]
Output: 3

Example 5:

Input: grid = [[4]]
Output: 0

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 100

这个题目路有点难,我想了一下午,不过想明白之后也就不难了。

给定一个网格地图,每个网格标明了从该位置可以到达的下一个位置。现在要从地图的左上角走到右下角,有可能根据图标不能到达,问最少改变几个图标可以到达。每个图标仅可改变一次,可将其改为上下左右任意一个方向。

解法1。事实上,对于网格中的每个点,它都可以走到其上下左右的4个点,只不过按图标走费用为0,不按图标走费用为1。所以可以构造一个长和宽都是m*n的图,总元素即为(m*n)*(m*n),新的图中的值就是老图中相邻两个点之间的费用。然后在新的图中执行Dijkstra算法,寻找最短路径,即为原图的最小消费。

解法1太复杂了,忘记怎么写Dijkstra了,放弃。

解法2。不转换为新的图,直接在原图中执行算法。设置一个和原图同样大小的cost二维数组,cost[i][j]表示从左上角走到该位置的最小费用。对原图从左上角开始执行BFS,如果从当前点走到下一个节点的累加费用小于下一个节点的当前费用,则更新下一个节点的费用,并且BFS走到下一个节点。最终右下角的费用就是最小费用。

完整代码如下:

const int MAXCOST = 1000000;
struct Node {
	int x, y;
	Node() :x(0), y(0) {};
	Node(int x_, int y_) :x(x_), y(y_){};
};
class Solution {
public:

	int minCost(vector<vector<int>>& grid) {
		vector<vector<int>> dirs = { {0, 1}, {0, -1},{1, 0},{-1, 0} };//右左下上
		int m = grid.size(), n = grid[0].size();
		vector<vector<int>> cost(m, vector<int>(n, MAXCOST));
		cost[0][0] = 0;
		queue<Node> q;
		q.push(Node());
		while (!q.empty()) {
			Node cur = q.front();
			q.pop();
			for (int i = 0; i < dirs.size(); ++i) {
				int x = cur.x + dirs[i][0], y = cur.y + dirs[i][1];
				if (x < 0 || x >= m || y < 0 || y >= n)continue;
				if (i + 1 == grid[cur.x][cur.y] && cost[cur.x][cur.y] < cost[x][y]) {
					cost[x][y] = cost[cur.x][cur.y];
					q.push(Node(x, y));
				}
				else if (i + 1 != grid[cur.x][cur.y] && (cost[cur.x][cur.y] + 1 < cost[x][y])) {
					cost[x][y] = cost[cur.x][cur.y] + 1;
					q.push(Node(x, y));
				}
			}
		}
		return cost[m - 1][n - 1];
	}
};

本代码提交AC,用时76MS。

该解法没有设置visited数组标记走过的节点,因为没必要。一方面,以题目中的Example 2的图为例(下标从1开始),其(2,1)位置有可能从(1,1)直接下来,也有可能(1,1)先走右边下来再走左边到达(2,1),而第二条路径的费用低于第一条路径,如果设置visited数组的话,第二条路径就没法走了。另一方面,也不用担心不设置visited数组会导致程序来回来反复跑而无法终止,因为只有当走到该点的费用小于该点的当前费用时,才会将其加入队列,如果来回跑的话,费用肯定大于改点当前费用,也就不会加入队列了。所以无需设置visited数组,程序也能正常结束并得到正确结果。

解法3。参考讨论区题解,和解法2类似,但比解法2效率更高。BFS的时候,使用双端队列存储需要访问的节点,而且每次插入队列时,先插入满足原图方向标识的节点,而且是插入到队列开头,因为满足方向标识的路径费用为0,插入到队列开头之后,下次BFS时也先从队列开头取节点。对于不满足方向标识的节点,插入到队列尾部。这个策略相当于优先走费用为0的路径,只有当这些路径无法到达时,才会走费用为1的路径。

完整代码如下:

struct Node {
	int x, y, cost;
	Node() :x(0), y(0), cost(0) {};
	Node(int x_, int y_, int cost_) :x(x_), y(y_), cost(cost_) {};
};
class Solution {
public:

	int minCost(vector<vector<int>>& grid) {
		vector<vector<int>> dirs = { {0, 1}, {0, -1},{1, 0},{-1, 0} };//右左下上
		int m = grid.size(), n = grid[0].size();
		vector<vector<int>> flag(m, vector<int>(n, 0));
		deque<Node> q;
		q.push_back(Node());
		int ans = 0;
		while (!q.empty()) {
			Node cur = q.front();
			q.pop_front();
			flag[cur.x][cur.y] = 1;
			if (cur.x == m - 1 && cur.y == n - 1) {
				ans = cur.cost;
				break;
			}
			for (int i = 0; i < dirs.size(); ++i) {
				int x = cur.x + dirs[i][0], y = cur.y + dirs[i][1];
				if (x < 0 || x >= m || y < 0 || y >= n)continue;
				if (flag[x][y] == 1)continue;
				if (i + 1 == grid[cur.x][cur.y]) {
					q.push_front(Node(x, y, cur.cost));
				}
				else {
					q.push_back(Node(x, y, cur.cost + 1));
				}
			}
		}
		return ans;
	}
};

本代码提交AC,用时36MS,比解法2效率更高。