【问题标题】:Don't know how to use a hash table to solve a problem不知道如何使用哈希表解决问题
【发布时间】:2021-08-13 03:55:25
【问题描述】:

我必须使用哈希表解决以下问题(在 C 中)(我假设使用哈希表,因为我们现在正在研究哈希表):

输入的第一行有2个数字:nm

接下来,我们输入带有 m 个数字的 n 行。 (所以一个 n*m 矩阵) 我们必须从左上角走到右下角(只能向南或向东移动)。我们穿过的每个单元格要么将单元格中的数字添加到变量“s”中,要么减少它。因此,如果我们遍历一个带有 -5 的单元格,我们将得到 s-5 ,如果我们遍历一个带有 +3 的单元格,我们将得到 s+3。一开始,s是左上角的数字,总是>0。另一个规则是我们不能遍历数字为 0 的单元格。此外,每次离开单元格时都必须减 1,因此每次离开单元格时都会得到 s-1。 输出必须是到达右下角后可以得到的最大s。 这是输入/输出的示例:

保证至少有一条到右下角的路径最终会给出一个s至少等于1,所以如果最后是s 是否定的路径肯定是错误的。

我很难解决这个问题(尤其是使用哈希表),因此非常感谢任何帮助。另外,有没有其他更有效的解决方法?

【问题讨论】:

  • 这看起来不像是一个需要哈希表的练习。看起来应该只是数组。
  • 除非有很多 0 条目可以从哈希表中省略,否则效率会更高。
  • 如果您的评估是正确的,这听起来像是在试图让您将其实现为递归函数,将其结果存储在哈希表中。不过,在这个特定实例中,哈希表相对于二维数组没有任何好处。
  • @selbie 您可能错过了“每次我们离开牢房时都会有 s-1”部分。我猜路径是 10-1 -6-1 +2-1 -1-1 +1-1 +3-1 -1 = 2。
  • 或者,因为到达终点需要 6 步,所以无论走哪条路,您都可以简单地从 10 - 6 开始。然后答案是 4-6+2-1+1+3-1=2。在每次移动时减 1 的唯一原因是,如果存在部分和在有效路径上不能为负数的限制(例如,如果正数是获得食物,负数是失去食物,移动需要一个食物)。问题描述中没有说明部分和不能为负的限制,因此每次移动减一个的要求相当愚蠢。

标签: c data-structures hashtable


【解决方案1】:

正如 Mark 提到的,这是动态规划问题。所以这个问题与哈希表无关。现在,由于马克的回答有点难以理解,我将尝试解释我的解决方案。 给定的问题类似于标准矩阵路径优化问题,但有两个有趣的转折。

  1. 解决方案路径不能包含0 valued cells
  2. 高于点也是一个标准问题。但这就是转折点。由于单元格具有整数值。由于之前的加法和减法运算,很难区分原始 0 valued cells 和中间 dp 矩阵中的那些。

所以为了解决上述问题,我们需要单独存储原始0 valued cells 的索引。最简单的方法是创建另一个参考矩阵并标记0 valued cells

现在,我们应用简单的动态规划技术。

  1. dp[i][j]= dp[i][j] + max(dp[i-1][j],dp[i][j-1]) - 1;
  2. if (zeroed[i][j] == 1)0 valued cell 然后忽略此单元格。
  3. if (zeroed[i-1][j] == 1) 然后忽略顶部单元格的添加。
  4. if (zeroed[i][j-1] == 1) 然后忽略左侧单元格的添加。
  5. dp[row-1][col-1] 是优化答案。

这就是我们解决这个问题的方法。如果你仍然觉得它很难,那么你需要学习动态规划。 程序代码:

#include<iostream>
using namespace std;
int zeroed[50][50]; //for reference of 0 valued cells
int main(){
        int dp[50][50];
        int row,col,value;
        cin>>row>>col;
        /*=========initializing matrix========*/
        for(int i=0;i<row;i++){
                for(int j=0;j<col;j++){

                        cin>>value;
                        dp[i][j]=value;
                        if(value == 0){
                                zeroed[i][j]=1; //marking 0 valued cell
                        }

                }
        }

        /*==========applying dynamic programming=====*/
        for(int i=0;i<row;i++){
                for(int j=0;j<col;j++){
                        if(zeroed[i][j]== 1){
                                continue; //just ignore this cells
                        }
                        if(i>0 && j>0){
                                if(zeroed[i-1][j] !=1 && zeroed[i][j-1]!=1){
                                        dp[i][j]+=max(dp[i-1][j],dp[i][j-1]) - 1;
                                }else if(zeroed[i-1][j]!=1){
                                        dp[i][j]+= dp[i-1][j] - 1;
                                }else if(zeroed[i][j-1]!=1){
                                        dp[i][j]+=dp[i][j-1] - 1;
                                }
                                //ignore other cases

                        }else if(i>0){
                                if(zeroed[i-1][j]!=1){
                                        dp[i][j]+=dp[i-1][j] - 1;
                                }
                                //ignore other cases
                        }else if(j>0){
                                if(zeroed[i][j-1]!=1){

                                        dp[i][j]+=dp[i][j-1] - 1;
                                }
                                //ignore other cases
                        }
                }
        }
        cout<<dp[row-1][col-1];//max s 
        return 0;
}

【讨论】:

  • 忘记了你要求 c 程序。但是你可以很容易地用 c 语法替换 c++ 输入。
【解决方案2】:

这似乎是一个非常简单的动态规划问题。

让左上角为(0, 0) 和右下角(n - 1, m - 1),并让arr[i][j](i, j) 位置的数字。然后对于所有i, j,例如0 &lt;= i &lt; n0 &lt;= j &lt; m,将f(i, j) 定义为最大可能的s,以便从位置(0, 0) 到位置(i, j)-1,如果这是不可能的.

如果previousS = INT_MINvalueInCell = 0,则将combine(previousS, valueInCell) 定义为INT_MIN,否则将previousS + valueInCell - 1

那么我们看到以下是真的:

f(0, 0) = arr[0, 0]
f(i, 0) = combine(f(i - 1, 0), arr[i][0]) 适用于所有 1 &lt;= i &lt; n
f(j, 0) = combine(f(j - 1, 0), arr[0][j]) 适用于所有 1 &lt;= j &lt; m
f(i, j) = combine(max(f(i - 1, j), f(i, j - 1)), arr[i][j]) 适用于所有 1 &lt;= i &lt; n1 &lt;= j &lt; m

特别是,我们正在寻找f(n - 1, m - 1)

现在这是一个递归算法,但是递归会非常低效,因为我们每次最多可以进行 2 次递归调用。所以我们将定义一个数组f[i][j] 来保存f 的值。

int combine(int previous_s, int value_in_cell) {
    return previous_s == INT_MIN || value_in_cell == 0 ? INT_MIN : previous_s + value_in_cell - 1;
}

int max(int i, int j) {
    return i > j ? i : j;
}

int computeS(int n, int m, int** arr) {
    int** const f = malloc(n * sizeof *f);
    int** const end_f = f + n;
    for(int** j = f; j < end_f; j++) {
        *j = malloc(m * sizeof **j);
    }
    f[0][0] = arr[0][0];
    for(int i = 1; i < n; i++) {
        f[i][0] = combine(f[i - 1][0], arr[i][0]);
    }
    for(int j = 1; j < m; j++) {
        f[0][j] = combine(f[0][j - 1], arr[0][j]);
    }
    for(int i = 1; i < n; i++) {
        for(int j = 1; j < m; j++) {
            f[i][j] = combine(max(f[i - 1][j], f[i][j - 1]), arr[i][j]);
        }
    }
    int const ret_val = f[n - 1][m - 1];
    for(int** j = f; j < end_f; j++) {
        free(*j);
    }
    free(f);
    return ret_val;
}

如您所见,不需要哈希表。

执行 I/O 的代码:

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    int** const arr = malloc(n * sizeof *arr);
    int** const end_arr = arr + n;
    for(int** j = arr; j < end_arr; j++) {
        *j = malloc(m * sizeof **j);
        for(int* k = *j; k < *j + m; k++) {
            scanf("%d", k);
        }
    }
    
    printf("%d\n", computeS(n, m, arr));
    
    for(int**j = arr; j < end_arr; j++) {
        free(*j);
    }
    free(arr);
    
    return 0;
}

【讨论】:

    猜你喜欢
    • 2021-08-30
    • 1970-01-01
    • 2020-06-13
    • 2020-04-06
    • 2019-08-13
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    相关资源
    最近更新 更多