[算法]最小化初始点

最小化初始点

问题地址

题目描述

Description

Given a grid with each cell consisting of positive, negative or no points i.e, zero points. We can move across a cell only if we have positive points ( > 0 ). Whenever we pass through a cell, points in that cell are added to our overall points. We need to find minimum initial points to reach cell (m-1, n-1) from (0, 0) by following these certain set of rules :

1.From a cell (i, j) we can move to (i+1, j) or (i, j+1).

2.We cannot move from (i, j) if your overall points at (i, j) is <= 0.

3.We have to reach at (n-1, m-1) with minimum positive points i.e., > 0.

Input

The first line contains an integer ‘T’ denoting the total number of test cases.In each test cases, the first line contains two integer ‘R’ and ‘C’ denoting the number of rows and column of array.
The second line contains the value of the array i.e the grid, in a single line separated by spaces in row major order.

Constraints:

1 ≤ T ≤ 30

1 ≤ R,C ≤ 10

-30 ≤ A[R][C] ≤ 30

Input: points[m][n] =

{ {-2, -3, 3},
{-5, -10, 1},
{10, 30, -5}};

Output

Print the minimum initial points to reach the bottom right most cell in a separate line.

7

Explanation:
7 is the minimum value to reach destination with
positive throughout the path. Below is the path.

(0,0) -> (0,1) -> (0,2) -> (1, 2) -> (2, 2)

We start from (0, 0) with 7, we reach(0, 1)
with 5, (0, 2) with 2, (1, 2) with 5, (2, 2)with and finally we have 1 point (we needed
greater than 0 points at the end).

Sample Input 1

1
3 3
-2 -3 3 -5 -10 1 10 30 -5

Sample Output 1

7
题目解析

这道题和格子里的整数类似,都有点像最大最小代价问题,但又都不一样

  1. 给定一个数组,由多个数字组成
  2. ,假设一个值为X,代表我们的生命
  3. 从array[0] [0]开始,向右或者向下移动,每次只能移动一步,
  4. 每移动一次,将X加上当前格子里的数array[i] [j] (当然如果这里的数是负数就要扣掉我们的生命)
  5. 最终目标是走到最后一个格子即:array[-1] [-1]
  6. 问能到达最后一个格子,所需要的初始血量X,最少是多少
  7. 到达每个点,更新后的值必须要大于1,任意时刻(哪怕是最后一个格子),小于1则不行
思路解析

这里先引用一大段话,出自文章头部连接

  • At the first look, this problem looks similar Max/Min Cost Path, but maximum overall points gained will not guarantee the minimum initial points. Also, it is compulsory in the current problem that the points never drops to zero or below. For instance, Suppose following two paths exists from source to destination cell.

  • We can solve this problem through bottom-up table filling dynamic programing technique.

    • To begin with, we should maintain a 2D array dp of the same size as the grid, where dp[i] [j] represents the minimum points that guarantees the continuation of the journey to destination before entering the cell (i, j). It’s but obvious that dp[0] [0] is our final solution. Hence, for this problem, we need to fill the table from the bottom right corner to left top.
    • Now, let us decide minimum points needed to leave cell (i, j) (remember we are moving from bottom to up). There are only two paths to choose: (i+1, j) and (i, j+1). Of course we will choose the cell that the player can finish the rest of his journey with a smaller initial points. Therefore we have: min_Points_on_exit = min(dp[i+1] [j], dp[i] [j+1])
  • Now we know how to compute min_Points_on_exit, but we need to fill the table dp[][] to get the solution in dp[0] [0].

  • How to compute dp[i] [j]?
    The value of dp[i] [j] can be written as below.

  • dp[i] [j] = max(min_Points_on_exit – points[i] [j], 1)

  • Let us see how above expression covers all cases.

    • If points[i] [j] == 0, then nothing is gained in this cell; the player can leave the cell with the same points as he enters the room with, i.e. dp[i] [j] = min_Points_on_exit.
    • If points[i] [j] < 0, then the player must have points greater than min_Points_on_exit before entering (i, j) in order to compensate for the points lost in this cell. The minimum amount of compensation is " – points[i] [j] ", so we have dp[i] [j] = min_Points_on_exit – points[i] [j].
    • If points[i] [j] > 0, then the player could enter (i, j) with points as little as min_Points_on_exit – points[i] [j]. since he could gain “points[i] [j]” points in this cell. However, the value of min_Points_on_exit – points[i] [j] might drop to 0 or below in this situation. When this happens, we must clip the value to 1 in order to make sure dp[i][j] stays positive:
      dp[i][j] = max(min_Points_on_exit – points[i] [j], 1).
  • Finally return dp[0] [0] which is our answer.

  1. 这道题要求最小的初始值是什么,那我们最后要输出的一定是dp[0] [0],
  2. 从1知,这个dp数组一定是倒着更新的
  3. 倒着想一下,到达最后一个点,要么是从上面过来的,要么是从左边过来的,
构建dp数组
  1. dp数组与原始数据点的数组大小规模一致.
  2. dp[i] [j]代表若能到达终点,则到达当前点(i,j)所需要的最小生命值

核心转移方程如下
m i n _ p o i n t = m i n ( d p [ i + 1 ] [ j ] , d p [ i ] [ j + 1 ] ) d p [ i ] [ j ] = m a x ( m i n _ p o i n t – m a t r i x [ i ] [ j ] , 1 ) min\_point = min(dp[i + 1][j], dp[i][j + 1])\\ dp[i] [j] = max(min\_point – matrix[i] [j], 1) min_point=min(dp[i+1][j],dp[i][j+1])dp[i][j]=max(min_pointmatrix[i][j],1)
可以看下面例子

详细构建步骤
  1. 创建dp数组
  2. 先把dp[-1] [-1]初始化,这里分两种情况
    1. 如果matrix[-1] [-1]是小于0的,那么到达这个点时,值一定要为abs(matrix[-1] [-1])+1
    2. 如果matrix[-1] [-1]是大于0的,那么到达这个点时,值为1即可
  3. 初始化最后一行和最后一列,
  4. 按照转移方程更新
matrix 矩阵
[[ -2  -3   3]
 [ -5 -10   1]
 [ 10  30  -5]]
 
dp数组 
初始状态-->添加最后一个点的值-->初始化最后一行和最后一列
[[0 0 0]	[[0 0 0]	[[0 0 2]
 [0 0 0]	 [0 0 0]	 [0 0 5]
 [0 0 0]]	 [0 0 6]]	 [1 1 6]]
 
更新倒数第二行-->更新倒数第一行
[[ 0  0  2]		[[ 7  5  2]
 [ 6 11  5]	 	 [ 6 11  5]
 [ 1  1  6]]	 [ 1  1  6]]

举个例子,比如说初始化到dp[2][1]时
[[0 0 0]
 [0 0 0]
 [0 * 6]] 
 该位置到达dp[2][2]所需要的最小生命值为 6 -30 =-24 但因为生命值不能为负数,因此这里的最小生命值变成1


再举个例子,比如说更新dp[1][1]时
[[ 0  0  0]		
 [ 0  *  5]	 	
 [ 1  1  6]]
 假设从dp[1][1]到dp[1][2],我们需要有 5 -(-10) =15点生命值才可以
 假设从dp[1][1]到dp[2][1],我们需要有 1 -(-10) =11点生命值才可以
 因此dp[1][1]最小值是11
代码实现
if __name__ == '__main__':
    for _ in range(int(input())):
        # 读取数据
        rn, cn = list(map(int, input().strip().split(" ")))
        arr = list(map(int, input().strip().split(" ")))
        matrix = [[0] * cn for _ in range(rn)]
        k = 0
        for i in range(rn):
            for j in range(cn):
                matrix[i][j] = arr[k]
                k += 1
        # 初始化dp
        # 1.初始化最后一个点
        dp = [[0] * cn for _ in range(rn)]
        # 因为我们要保证到最后一个格子时,若加上最后一个格子的数,正好为1(为1则是刚好到,符合题意)
        # 如果最后一个格子是正数,那么值直接就是1即可
        # 如果最后一个格子负数,那么值为负数绝对值+1时(相加正好为1)
        if matrix[-1][-1] > 0:  # 如果最后一个数是大于0的,
            dp[-1][-1] = 1  # 那么初始化为1
        else:  # 如果小于0
            dp[-1][-1] = abs(matrix[-1][-1]) + 1  # 则初始化为绝对值加一
        # 2.初始化最后一行和最后一列
        for i in range(cn - 2, -1, -1):  # 倒序更新
            dp[-1][i] = max(dp[-1][i + 1] - matrix[-1][i], 1)
        for i in range(rn - 2, -1, -1):
            dp[i][-1] = max(dp[i + 1][-1] - matrix[i][-1], 1)
        # dp
        for i in range(rn - 2, -1, -1):
            for j in range(cn - 2, -1, -1):
                min_point = min(dp[i + 1][j], dp[i][j + 1])  # 转移到右面或者转移到下边所需要的最小值
                dp[i][j] = max(min_point - matrix[i][j], 1)  #
        print(dp[0][0])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值