📜  在遇到死胡同之前收集最大的硬币

📅  最后修改于: 2021-09-17 07:13:17             🧑  作者: Mango

给定一个字符矩阵,其中每个单元格都具有以下值之一。

'C' -->  This cell has coin

'#' -->  This cell is a blocking cell. 
         We can not go anywhere from this.

'E' -->  This cell is empty. We don't get
         a coin, but we can move from here.  

初始位置为单元格 (0, 0),初始方向为右。

以下是跨单元格移动的规则。

如果 face 是 Right,那么我们可以移动到下面的单元格

  1. 向前移动一步,即单元格 (i, j+1) 并且方向保持正确。
  2. 向下移动一步并面向左,即单元格 (i+1, j) 并且方向变为左。

    如果 face 是 Left,那么我们可以移动到下面的单元格

    1. 向前移动一步,即单元格 (i, j-1) 并且方向保持向左。
    2. 向下移动一步并面向右侧,即单元格 (i+1, j) 并且方向变为正确。

    最终位置可以是任何地方,最终方向也可以是任何东西。目标是收集最大的硬币。

    例子:
    例子

    我们强烈建议您将浏览器最小化,然后自己先尝试一下。

    上述问题可以递归定义如下:

    maxCoins(i, j, d):  Maximum number of coins that can be 
                        collected if we begin at cell (i, j)
                        and direction d.
                        d can be either 0 (left) or 1 (right)
    
       // If this is a blocking cell, return 0. isValid() checks
       // if i and j are valid row and column indexes.
       If (arr[i][j] == '#' or isValid(i, j) == false)
           return 0
    
       // Initialize result
       If (arr[i][j] == 'C')
           result = 1;
       Else 
           result = 0;
    
       If (d == 0) // Left direction 
           return result +  max(maxCoins(i+1, j, 1),  // Down
                                maxCoins(i, j-1, 0)); // Ahead in left
    
       If (d == 1) // Right direction 
           return result +  max(maxCoins(i+1, j, 1),  // Down
                                maxCoins(i, j+1, 0)); // Ahead in right
    

    下面是上述递归算法的 C++ 实现。

    C++
    // A Naive Recursive C++ program to find maximum number of coins
    // that can be collected before hitting a dead end
    #include
    using namespace std;
    #define R 5
    #define C 5
      
    // to check whether current cell is out of the grid or not
    bool isValid(int i, int j)
    {
        return (i >=0 && i < R && j >=0 && j < C);
    }
      
    // dir = 0 for left, dir = 1 for facing right.  This function returns
    // number of maximum coins that can be collected starting from (i, j).
    int maxCoinsRec(char arr[R][C],  int i, int j, int dir)
    {
        // If this is a invalid cell or if cell is a blocking cell
        if (isValid(i,j) == false || arr[i][j] == '#')
            return 0;
      
        // Check if this cell contains the coin 'C' or if its empty 'E'.
        int result = (arr[i][j] == 'C')? 1: 0;
      
        // Get the maximum of two cases when you are facing right in this cell
        if (dir == 1) // Direction is right
           return result + max(maxCoinsRec(arr, i+1, j, 0),     // Down
                                 maxCoinsRec(arr, i, j+1, 1));  // Ahead in right
      
        // Direction is left
        // Get the maximum of two cases when you are facing left in this cell
         return  result + max(maxCoinsRec(arr, i+1, j, 1),    // Down
                               maxCoinsRec(arr, i, j-1, 0));  // Ahead in left
    }
      
    // Driver program to test above function
    int main()
    {
        char arr[R][C] = { {'E', 'C', 'C', 'C', 'C'},
                           {'C', '#', 'C', '#', 'E'},
                           {'#', 'C', 'C', '#', 'C'},
                           {'C', 'E', 'E', 'C', 'E'},
                           {'C', 'E', '#', 'C', 'E'}
                         };
      
       // As per the question initial cell is (0, 0) and direction is
        // right
        cout << "Maximum number of collected coins is "
             << maxCoinsRec(arr, 0, 0, 1);
      
        return 0;
    }


    Python3
    # A Naive Recursive Python 3 program to 
    # find maximum number of coins 
    # that can be collected before hitting a dead end 
    R= 5
    C= 5
      
    # to check whether current cell is out of the grid or not 
    def isValid( i, j): 
      
        return (i >=0 and i < R and j >=0 and j < C) 
      
      
    # dir = 0 for left, dir = 1 for facing right. 
    # This function returns 
    # number of maximum coins that can be collected
    # starting from (i, j). 
    def maxCoinsRec(arr, i, j, dir): 
      
        # If this is a invalid cell or if cell is a blocking cell 
        if (isValid(i,j) == False or arr[i][j] == '#'): 
            return 0
      
        # Check if this cell contains the coin 'C' or if its empty 'E'. 
        if (arr[i][j] == 'C'):
            result=1
        else:
            result=0
      
        # Get the maximum of two cases when you are facing right in this cell 
        if (dir == 1):
              
         # Direction is right 
            return (result + max(maxCoinsRec(arr, i+1, j, 0),
                   maxCoinsRec(arr, i, j+1, 1))) 
      
        # Direction is left 
        # Get the maximum of two cases when you are facing left in this cell 
        return (result + max(maxCoinsRec(arr, i+1, j, 1),
               maxCoinsRec(arr, i, j-1, 0))) 
      
      
    # Driver program to test above function 
    if __name__=='__main__':
        arr = [ ['E', 'C', 'C', 'C', 'C'],
              ['C', '#', 'C', '#', 'E'],
              ['#', 'C', 'C', '#', 'C'],
              ['C', 'E', 'E', 'C', 'E'],
              ['C', 'E', '#', 'C', 'E'] ] 
      
        # As per the question initial cell is (0, 0) and direction is 
        # right 
        print("Maximum number of collected coins is ", maxCoinsRec(arr, 0, 0, 1)) 
      
    # this code is contributed by ash264


    C++
    // A Dynamic Programming based C++ program to find maximum
    // number of coins that can be collected before hitting a
    // dead end
    #include
    using namespace std;
    #define R 5
    #define C 5
      
    // to check whether current cell is out of the grid or not
    bool isValid(int i, int j)
    {
        return (i >=0 && i < R && j >=0 && j < C);
    }
      
    // dir = 0 for left, dir = 1 for right.  This function returns
    // number of maximum coins that can be collected starting from
    // (i, j).
    int maxCoinsUtil(char arr[R][C],  int i, int j, int dir,
                     int dp[R][C][2])
    {
        // If this is a invalid cell or if cell is a blocking cell
        if (isValid(i,j) == false || arr[i][j] == '#')
            return 0;
      
        // If this subproblem is already solved than return the
        // already evaluated answer.
        if (dp[i][j][dir] != -1)
           return dp[i][j][dir];
      
        // Check if this cell contains the coin 'C' or if its 'E'.
        dp[i][j][dir] = (arr[i][j] == 'C')? 1: 0;
      
        // Get the maximum of two cases when you are facing right
        // in this cell
        if (dir == 1) // Direction is right
           dp[i][j][dir] += max(maxCoinsUtil(arr, i+1, j, 0, dp), // Down
                                maxCoinsUtil(arr, i, j+1, 1, dp)); // Ahead in rught
      
        // Get the maximum of two cases when you are facing left
        // in this cell
        if (dir == 0) // Direction is left
           dp[i][j][dir] += max(maxCoinsUtil(arr, i+1, j, 1, dp),  // Down
                                maxCoinsUtil(arr, i, j-1, 0, dp)); // Ahead in left
      
        // return the answer
        return dp[i][j][dir];
    }
      
    // This function mainly creates a lookup table and calls
    // maxCoinsUtil()
    int maxCoins(char arr[R][C])
    {
        // Create lookup table and initialize all values as -1
        int dp[R][C][2];
        memset(dp, -1, sizeof dp);
      
        // As per the question initial cell is (0, 0) and direction
        // is right
        return maxCoinsUtil(arr, 0, 0, 1, dp);
    }
      
    // Driver program to test above function
    int main()
    {
        char arr[R][C] = { {'E', 'C', 'C', 'C', 'C'},
                           {'C', '#', 'C', '#', 'E'},
                           {'#', 'C', 'C', '#', 'C'},
                           {'C', 'E', 'E', 'C', 'E'},
                           {'C', 'E', '#', 'C', 'E'}
                         };
      
      
        cout << "Maximum number of collected coins is "
             << maxCoins(arr);
      
        return 0;
    }
    Output:Maximum number of collected coins is 8Time Complexity of above solution is O(R x C x d). Since d is 2, time complexity can be written as O(R x C).Thanks to Gaurav Ahirwar for suggesting above solution.In case you wish to attend live classes with experts, please refer DSA Live Classes for Working Professionals and Competitive Programming Live for Students.


    输出:

    Maximum number of collected coins is 8

    上述解递归的时间复杂度是指数级的。我们可以使用动态规划在多项式时间内解决这个问题。这个想法是使用 3 维表 dp[R][C][k] 其中 R 是行数,C 是列数,d 是方向。下面是基于动态编程的 C++ 实现。

    C++

    // A Dynamic Programming based C++ program to find maximum
    // number of coins that can be collected before hitting a
    // dead end
    #include
    using namespace std;
    #define R 5
    #define C 5
      
    // to check whether current cell is out of the grid or not
    bool isValid(int i, int j)
    {
        return (i >=0 && i < R && j >=0 && j < C);
    }
      
    // dir = 0 for left, dir = 1 for right.  This function returns
    // number of maximum coins that can be collected starting from
    // (i, j).
    int maxCoinsUtil(char arr[R][C],  int i, int j, int dir,
                     int dp[R][C][2])
    {
        // If this is a invalid cell or if cell is a blocking cell
        if (isValid(i,j) == false || arr[i][j] == '#')
            return 0;
      
        // If this subproblem is already solved than return the
        // already evaluated answer.
        if (dp[i][j][dir] != -1)
           return dp[i][j][dir];
      
        // Check if this cell contains the coin 'C' or if its 'E'.
        dp[i][j][dir] = (arr[i][j] == 'C')? 1: 0;
      
        // Get the maximum of two cases when you are facing right
        // in this cell
        if (dir == 1) // Direction is right
           dp[i][j][dir] += max(maxCoinsUtil(arr, i+1, j, 0, dp), // Down
                                maxCoinsUtil(arr, i, j+1, 1, dp)); // Ahead in rught
      
        // Get the maximum of two cases when you are facing left
        // in this cell
        if (dir == 0) // Direction is left
           dp[i][j][dir] += max(maxCoinsUtil(arr, i+1, j, 1, dp),  // Down
                                maxCoinsUtil(arr, i, j-1, 0, dp)); // Ahead in left
      
        // return the answer
        return dp[i][j][dir];
    }
      
    // This function mainly creates a lookup table and calls
    // maxCoinsUtil()
    int maxCoins(char arr[R][C])
    {
        // Create lookup table and initialize all values as -1
        int dp[R][C][2];
        memset(dp, -1, sizeof dp);
      
        // As per the question initial cell is (0, 0) and direction
        // is right
        return maxCoinsUtil(arr, 0, 0, 1, dp);
    }
      
    // Driver program to test above function
    int main()
    {
        char arr[R][C] = { {'E', 'C', 'C', 'C', 'C'},
                           {'C', '#', 'C', '#', 'E'},
                           {'#', 'C', 'C', '#', 'C'},
                           {'C', 'E', 'E', 'C', 'E'},
                           {'C', 'E', '#', 'C', 'E'}
                         };
      
      
        cout << "Maximum number of collected coins is "
             << maxCoins(arr);
      
        return 0;
    }
    

    输出:

    Maximum number of collected coins is 8

    上述解决方案的时间复杂度为 O(R x C xd)。由于 d 是 2,时间复杂度可以写为 O(R x C)。

    感谢 Gaurav Ahirwar 提出上述解决方案。

    如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程