数组中等数之间的最大距离

时间:2009-08-20 14:20:10

标签: c# algorithm

假设我有一个像这个例子的矩阵(数组),但更大:

0 0 5 0 3 6 6 4 0 3 0 8 0 1 1
9 4 0 6 0 0 0 4 1 0 6 0 7 0 0
3 1 6 1 5 0 8 0 8 0 3 2 6 4 8
1 0 2 2 8 5 8 1 8 7 4 1 0 3 0
6 3 8 1 0 0 4 0 0 3 1 5 2 0 0
0 0 5 0 3 6 6 4 0 3 0 8 0 1 1
9 4 0 6 0 0 0 4 1 0 6 0 7 0 0
3 1 6 1 5 0 8 0 8 0 3 2 6 4 8
1 0 2 2 8 5 8 1 8 7 4 1 0 3 0
6 3 8 1 0 0 4 0 9 4 1 5 2 0 0

0 0 5 0 3 6 6 4 0 3 0 8 0 1 1 9 4 0 6 0 0 0 4 1 0 6 0 7 0 0 3 1 6 1 5 0 8 0 8 0 3 2 6 4 8 1 0 2 2 8 5 8 1 8 7 4 1 0 3 0 6 3 8 1 0 0 4 0 0 3 1 5 2 0 0 0 0 5 0 3 6 6 4 0 3 0 8 0 1 1 9 4 0 6 0 0 0 4 1 0 6 0 7 0 0 3 1 6 1 5 0 8 0 8 0 3 2 6 4 8 1 0 2 2 8 5 8 1 8 7 4 1 0 3 0 6 3 8 1 0 0 4 0 9 4 1 5 2 0 0

我正在尝试确定两个相等数字的位置,它们在阵列中的对角线,水平或垂直直线之间的距离最大,距离计算为它们之间的数字计数(距离d> = 0)。

其他限制条件:

  • 如上所述的直线可能不包含标记其开头和结尾的相同数字,因此您不能 6 0 4 5 6 1 7 3 5 6 并且说距离6..6是8,因为序列中有6个。
  • 没有给出要查找的数字,但必须动态确定。

在示例中结果(将数组视为常规X | Y坐标系统,左下角为0,0)应确定P1(0,8),P2(8,0),d = 7(数字:9)。

关于如何有效地做到这一点的任何好主意?我正在使用C#,但其他语言的示例/想法也很受欢迎。

如果你想知道这个问题来自哪里,我一直在思考各种与数学相关的挑战,我认为这些挑战很难解决(对我自己而言),并希望通过了解别人如何解决这些问题来更好地处理这些问题。的问题。谢谢!

5 个答案:

答案 0 :(得分:11)

<强>算法:

简化问题。它相当于每行,列和对角线解算一维版本(在列表中找到相等值之间的最大间隙),然后返回最大值。

简化更多。一维版本非常简单。您只需构建一个将值映射到其位置列表的字典,解决每个值的“这个位置列表中最大的三角形”的微不足道的问题,然后返回最大值。

<强>分析:

小问题在位置列表的大小中占用线性时间(因为列表按[自然插入顺序]排序)。因此,1-dim间隙问题在值列表的大小中占用线性时间(因为每个值有一个位置)。因此,2-dim间隙问题需要矩阵大小的线性时间(因为每个值都包含在四个1-dim子问题中)。

因此,如果矩阵是n m,则解决方案将花费O(n m)时间。它需要O(n + m)空间(在每个1-dim阶段期间存储值 - >位置字典)。我真的怀疑你会做得更好(时间显然是最佳的,不太确定尺寸)。

答案 1 :(得分:2)

除了迭代每行,每列和每个对角线之外,我不知道你怎么能更快地做到这一点(你可以通过获取X的差值的绝对值来判断是否在同一对角线上有什么东西当然还有Y坐标,并跟踪你看到的每个数字的最新坐标,以及观察到的最大分离。

答案 2 :(得分:2)

以下主要思想是迭代数组一次,跟踪每行(hor),列(vert)和从上到下对角线(td)和从下到上对角线的最后找到的位置( DT)。有了这个,你只需找到每个方向到最后一个位置的距离,然后取最大值

修改: 另外一个注意事项,看起来问题是要求你弄清楚任何相同数字之间的最大距离,当我写出代码时我不明白。要解决这个问题,你只需要创建一个字典或数组(如果你知道可以存在的数字范围),为每个数字保存vert / hor / dt / td的集合,并使用那些而不是if yournum 。它仍然只需要一次通过数组。

int findmax(int[,] myarray, int height, int width, int yournum)
{
int diagnum = width + height - 1;

int[] vert = new int[width];
int[] hor  = new int[height];

int[] td   = new int[diagnum];
int[] dt   = new int[diagnum];

for (int x = 0; x < width; x++)
{
   vert[x] = -1;
}
for (int x = 0; x < height; x++)
{
   hor[x] = -1;
}
for (int x = 0; x < diagnum; x++)
{
   td[x] = -1;
   dt[x] = -1;
}

int maxlen = 0;

for (int x = 0; x < width; x++)
{
   for (int y = 0; y < height; y++)
   {
      if (myarray[y,x] == yournum)
      {
         if (vert[x] == -1)
         {
            vert[x] = y;
         }
         else
         {
            maxlen = Math.Max(maxlen, Math.Abs(y - vert[x] - 1));
            vert[x] = y;
         }
         if (hor[x] == -1)
         {
            hor[x] = y;
         }
         else
         {
            maxlen = Math.Max(maxlen, Math.Abs(x - hor[y] - 1));
            hor[y] = x;
         }

         int tdcol = x - y + height - 1;
         int tdloc = Math.Abs(Math.Max(0, tdcol - height + 1) - x);
         if (td[tdcol] == -1)
         {
            td[tdcol] = tdloc;
         }
         else
         {
            maxlen = Math.Max(maxlen, Math.Abs(tdloc - td[tdcol] - 1));
            td[tdcol] = tdloc;
         }
         int dtcol = y + x;
         int dtloc = Math.Abs(Math.Max(0,dtcol-height+1) - x);
         if (dt[dtcol] == -1)
         {
            dt[dtcol] = dtloc;
         }
         else
         {
            maxlen = Math.Max(maxlen, Math.Abs(dtloc - dt[dtcol] - 1));
            dt[dtcol] = dtloc;
         }
      }
   }
}
return maxlen;
}

编辑我仔细检查了方程式,但实际上并未尝试构建/测试代码,因此可能存在一些编译问题或逻辑问题,但看起来这应该可行。主要部分是使方程式正确,这使您可以多次遍历数组(即每个方向一个)。

答案 3 :(得分:1)

假设距离必须是两个相同数字之间的直接路径(即没有环形交叉口或无限循环)。

  1. 创建一个10的数组(对于出现在问题中的数字)
    • DIGIT_STRUCT[10]
  2. 为该数字的每个位置的每个数字创建一个数组。即对整个网格进行采样并按编号排列。
    • DIGIT_STRUCT[n].POSTITIONS[]
  3. 选出最大距离的那些位置的组合
    • 这是棘手的部分。但由于允许对角线移动,并且桌子是方形的,我相信你实际上可以按照距离原点(0,0)的距离对每个位置进行排名。你只需挑选出第一次和最后一次出现(按排名)。对每个数字列表来说都是一种排序
    • DIGIT_STRUCT[n].MAX_DIST {POSITION a, POSITION b}
  4. 确保每个MAX_DIST的至少一个最小路径存在而不会触及另一个位置编号。只有在整个行或列中填充了该数字时,才会阻止该路径。在您的6示例中,整个列在子网格上被另一个6阻止。
  5. 比较每个数字的最大距离。
  6. 并非所有工作都需要在这里完成,您不需要验证明显会更短的路径。如果路径无效,则不需要搜索不同的位置组合(如果另一个数字包含相同的距离路径)。

    如果找到网格大小的潜在路径(在您的示例中为16),您应该尝试验证那个,因为没有其他人会更长。

    验证路径应该需要两个子网格大小的数组,hor和vert,为每个vert [x]添加一个数字,并为子网格内DIGIT_STRUCT中的每个POSTITION(x,y)添加一个hor [y]。如果不存在等于子网格大小的vert [n]或hor [m],您就知道那里的最短路径不会被阻塞。

    修改

    忘了路径可能不是最理想的。如果存在类似

    的情况
    0 0 1 1
    1 0 1 1
    1 0 0 0
    1 1 1 0
    

    这里的答案是0,路径= 6,从(0,0)到(3,3)

    在这种情况下,您必须验证每个数字的路径,并且验证应返回距离。因为路径的距离可能会增加,所以你需要为每个数字验证更多的路径(距离最多可能是两倍)。

    Q值。你被迫走最直接的道路吗?在这种情况下(方形子网格),只有一个直接路径并且其被阻塞。如果你是你被迫这样做那么我认为你可以通过了解在验证过程中不会增加的路径来获得一些表现。

答案 4 :(得分:0)

由于您正在寻找最长的“细分”,因此请使用最长的细分来开始搜索,然后使用较短的细分。从大端开始,只要找到具有匹配结束号的段,就可以停止。这假设您只需返回一个更长或与任何其他段相等的单个段。