融合迷宫:最大周期

时间:2016-07-26 07:13:10

标签: algorithm data-structures

这个问题在一次采访中被问到,我仍在寻找最佳解决方案。

你有一个N细胞的迷宫。每个单元格可以有多个入口点但不超过一个出口 (即进入/退出点是像阀门一样的单向门)。

使用0的整数值命名单元格 到N-1。

您需要找到迷宫中最大周期的长度。如果没有循环则返回-1。

输入格式

  • 第一行具有单元数N
  • 第二行包含edge []数组的N个值列表。 edge [i]包含单元格编号 可以一步从单元格'i'到达。如果'i'单元格没有出口,则edge [i]为-1。

输出格式

  • 最大周期的长度。

示例输入:

23

4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21

示例输出

6

我已经尝试使用DFS执行此操作以查找所有可能的周期并打印最大周期大小。 如果有更好的解决方案,请告诉我。

13 个答案:

答案 0 :(得分:5)

给定图中的节点,从它开始有一个唯一的最大路径(因为任何节点最多只有一个出口)。它可能会也可能不会循环。

从节点开始很容易找到最终的循环长度:保持跟随退出节点,沿路径记录集合中的节点。如果找不到退出节点,或者您要访问以前访问过的节点,请停止。如果没有出口节点则没有循环,否则您可以通过从先前访问过的节点开始找到循环长度,并重新跟踪循环。 [你也可以在这里使用Floyd的算法,这需要O(1)而不是O(N)存储,但我们将在下一步中使用O(N)存储。]

使用它,可以在O(N)时间内找到最大循环:对图中的每个节点重复上述算法,但是缓存结果(如果没有找到循环,则存储-1)。如果在路径中找到先前缓存的结果,则必须小心停止上面的循环查找,并且一旦找到节点的结果,就必须缓存路径中所有节点的结果,直到找到节点的结果已经缓存。最大周期的大小是最大缓存值的值。

这是O(N)运行时:每个边(其中最多N个)在图中最多跟随3次,并且对于图中的每个节点,缓存只更新一次。它使用O(N)额外的存储空间。

答案 1 :(得分:1)

这是JavaScript中的一个实现。我没有使用JavaScript的任何奇特功能,因此可以从代码中轻松看到该算法。另一方面,它确实需要ES6支持才能运行(忘记IE):

function largestCycle(edges) {
    var result, visitedFrom, startCell, cell, cells;
    
    result = [];
    visitedFrom = Array(edges.length).fill(-1);
    for (startCell = 0; startCell < edges.length; startCell++) {
        cells = [];
        for (cell=startCell; cell>-1 && visitedFrom[cell]===-1; cell = edges[cell]) {
            visitedFrom[cell] = startCell;
            cells.push(cell);
        }
        if (cell > -1 && visitedFrom[cell] === startCell) {
            cells = cells.slice(cells.indexOf(cell));
            if (cells.length > result.length) result = cells;
        }
    }
    return result;
}

// Snippet I/O

var input = document.querySelector('textarea');
var output = document.querySelector('span');

(input.oninput = function () {
    // Get input as array of numbers
    var edges = input.value.trim().split(/\s+/).map(Number);
    // Apply algorithm
    var cycle = largestCycle(edges);
    // Output result
    output.textContent = cycle.length + ': ' + JSON.stringify(cycle);
})(); // Execute also at page load
Input:<br>
<textarea style="width:100%">4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21</textarea><br>
Greatest Cycle: <span></span>

这在 O(n)中运行。即使外部循环同时具有嵌套循环和迭代数组的表达式(使用sliceindexOf),这些子迭代仅在每个单元格中执行一次,因此总的来说这仍然是 O(n)的

该函数不仅返回循环大小,还返回包含属于该循环的单元格列表的数组。这是一个很小的开销,但允许更好地验证结果。

答案 2 :(得分:1)

trincot 建议的解决方案的 Python 实现。 说明:

  1. 遍历每个节点
  2. 对于每个节点,使用索引导航到下一个节点。例如(第一次迭代:外部 for 循环) 从第 0 个索引我们可以到达 4 ,从第 4 个索引我们可以到达 13 ,从第 13 个索引我们可以到达 11 ,依此类推,直到我们再次到达我们的案例 0 中的访问节点, viola ,我们找到了第一个循环。
  3. 检查是否将 visitedFrom[cell] == startCell 即 0 添加到 result 数组中。
  4. 重复下一个节点(步骤 1)

代码


def largestCycle(edges):
    result = []
    visitedFrom = [-1] * len(edges)
    for startCell in range(0, len(edges)):
        cells = []
        cell = startCell
        while cell > -1 and visitedFrom[cell] == -1:
            visitedFrom[cell] = startCell
            cells.append(cell)
            cell = edges[cell]
        if cell > -1 and visitedFrom[cell] == startCell:
            cells_idx = cells.index(cell)
            cells = cells[cells_idx:]
            if len(cells) > len(result):
                result = cells

    return result,len(result)

size = 23
edges = [4, 4, 1, 4, 13, 8, 8, 8, 0, 8, 14, 9, 15, 11, -1, 10, 15, 22, 22, 22, 22, 22, 21]
largestCycle(edges)

答案 3 :(得分:0)

import java.util.Scanner;
class path 
{
public static void main(String [] args)
{
    Scanner scnr=new Scanner(System.in);
    int n=scnr.nextInt();
    int a[]=new int[n];
    for(int i=0;i<n;i++)
    {
        a[i]=scnr.nextInt();
    }
    int path=-1;

    int j;
    for(int i=0;i<n;i++)
    {   j=i;
        int count=0;
        while(true)
        {   
            count++;
            if(a[j]==-1)
            {
                break;
            }
            else if(i==a[j])
            {
                if(count>path)
                path=count;
                break;
            }
            else
            {   int temp=j;
                j=a[j];
                a[temp]=-1;                 
            }
        }
    }
    System.out.println("my path: "+path);
}

}

答案 4 :(得分:0)

package graphsBasic;

import java.util.*;
public class LargestCycleInGraph {
    public static int checkCycle(int []cell , int size , int start) {
        Set<Integer> set = new HashSet<>();
        set.add(start);
        for(int i = start ;i< size;i++) {
            if( !set.contains(cell[i]) && cell[i] != -1) {
                set.add( cell[i] );
            }
            else return set.size() + 1; // 1 for again come to cycle node
        }
        return -1;
    }
    public static int findLargestCycle(int []cell , int size) {
        int max = -1;
        for(int i =0;i<size;i++) {
            //if you want to find sum of largest cycle return "Set" rather than its size and check for max sum
            int cyc = checkCycle(cell , size , i); 
            if(max < cyc)
                max = cyc;
          }
        return max;
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int size = 23;
        int []cell = {4, 4, 1, 4, 13, 8, 8, 8, 0, 8, 14, 9, 15, 11, -1, 10, 15, 22, 22, 22, 22, 22, 21};
        int largestCycle = findLargestCycle(cell , size);
        System.out.println("Largest cycle Length " +largestCycle);  
    }
}

答案 5 :(得分:0)

这是解决问题的方法,但输入格式实际上并不相同。

这是输入格式

test cases: N  
size of array: M  
array elements: 1<=a(i)<=M-1 where 0<=i<=M-1  
index to which last index points: C

在此问题中,我们必须在最大周期内对单元进行计数,这是代码:

class countLargestCycleMaze {

  static vertex[] cells;

  static class vertex {
    int name;
    neighbor list;
    public vertex(int v, neighbor nb) {
      this.name = v;
      this.list = nb;
    }
  }

  static class neighbor {
    int vnum;
    neighbor next;
    public neighbor(int v, neighbor nb) {
      this.vnum = v;
      this.next = nb;
    }
  }

  static int dfs(int v, int m) {
    neighbor tmp = cells[v].list;
    int c = 0;

    while (tmp.vnum != m)
      tmp = cells[tmp.vnum].list;

    tmp = cells[tmp.vnum].list;

    while (tmp.vnum != m) {
      tmp = cells[tmp.vnum].list;
      c++;
    }

    return c;
  }

  public static void main(String[] args) throws java.lang.Exception {
    try {
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

      int i, j, n, m, c;

      n = Integer.parseInt(br.readLine());
      while (n-- > 0) {
        m = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());
        c = Integer.parseInt(br.readLine());
        if (c == 0) {
          System.out.println("0");
          continue;
        }
        cells = new vertex[m + 1];
        for (i = 0; i < m; i++) {
          int num = Integer.parseInt(st.nextToken());
          cells[i] = new vertex(num, null);
          cells[i].list = new neighbor(num, cells[i].list);
        }
        cells[m] = new vertex(c, null);
        cells[m].list = new neighbor(c, cells[m].list);

        System.out.println(dfs(0, c));
      }
    } catch (Exception e) {}
  }
}

答案 6 :(得分:0)

def main():
    size = int(input())
    cell = input().split()

    for i in range(0, len(cell)):
        cell[i] = int(cell[i])
    m = -1
    for i in range(0, 23):
        if m < check_cycle(cell, i):
            m = check_cycle(cell, i)
    print("Largest cycle is made of", m, "nodes")


def check_cycle(cell, start):
    i = start
    if i in cell:
        cycle = [i]
        j = i
        while 1:
            for k in cycle:
                if cycle.count(k) >= 2:
                    if cycle[0] == cycle[-1]:
                        return len(cycle)-1
                    else:
                        return 0
            else:
                cycle.append(cell[j])
                j = cell[j]
    else:
        return 0


main()

答案 7 :(得分:0)

public static int solution(int arr[])
{
    ArrayList<Integer> sum = new ArrayList<>();
    for(int i = 0; i < arr.length; i ++)
    {
        ArrayList<Integer> path = new ArrayList<>();    
        int j = i;
        int tempSum = 0;
        while(arr[j]<arr.length && arr[j]!=i && arr[j]!=-1 && !path.contains(j))
        {   
            path.add(j);
            tempSum+=j;
            j=arr[j];
            if(arr[j]==i)
            {
                tempSum+=j;
                break;
            }
        }
        if(j<arr.length && i == arr[j])
            sum.add(tempSum);
    }
        if(sum.isEmpty())
            return -1;
        return Collections.max(sum);
}

答案 8 :(得分:0)

这是我尝试遍历图的每个节点:-

{
    "compilerOptions": {
        "checkJs": true,
        "allowJs": true,
        "outDir": "node_modules/.tmp/",
        "noImplicitAny": true
    },
    "include": [
        "index.js"
    ]
}

答案 9 :(得分:0)

O(n)时间复杂度解决方案仅在检查过是否访问过每个节点之后,才访问每个节点,因此每个节点仅被访问一次。

O(n)空间复杂度([n]:堆栈空间最大值+ [2 * n]:两个地图使用的最大大小)

要观察:两个节点之间始终存在唯一的路径(请检查任何测试用例),由于条件的限制,每个节点只有一个出口。

C ++代码:

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
//res stores result
int res = 0;
//visit to check in before visiting the node, to stop repeat visiting
unordered_map<int,bool> visit;
void dfs(vector<int> & a, unordered_map<int,int> &mp, int i, int k){
    if(visit.find(i) != visit.end())
        return;
    if(a[i] == -1){
        visit[i] = true;
        return;
    }
    if(mp.find(i) != mp.end()){
        res = max(res, k-mp[i]);
        visit[i] = true;
        return;
    }
    mp[i] = k;
    dfs(a, mp, a[i], k+1);
    visit[i] = true;
}

int main() {
    int n;
    cin>>n;
    vector<int> a(n,0);
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=0;i<n;i++){
        if(visit.find(i) == visit.end()){
            unordered_map<int,int> mp;
            dfs(a, mp, i, 0);
        }
    }
    cout<<res<<endl;
    return 0;
}

答案 10 :(得分:0)

C ++解决方案

#include <bits/stdc++.h>
using namespace std;
bool isCycle(vector<int> arr, int curr, vector<bool> &visited, vector<int> &path)
{
    if (curr == -1)
    {
        return false;
    }
    if (visited[curr])
    {
        return true;
    }
    visited[curr] = true;
    path.emplace_back(curr);
    if (isCycle(arr, arr[curr], visited, path))
        return true;
    return false;
}

int largestSumCycle(vector<int> arr)
{
    int n = arr.size();
    int sum = INT_MIN;
    vector<bool> visited(n, false);
    for (int i = 0; i < n; i++)
    {
        visited[i] = true;

        vector<int> path;

        if (isCycle(arr, arr[i], visited, path))
            sum = max(sum, accumulate(path.begin(), path.end(), 0));

        visited[i] = false;
    }
    if (sum == INT_MIN)
        return -1;
    return sum;
}

答案 11 :(得分:0)

这是访谈中的一个常见问题,在同一次访谈中,他们也针对相同的细节询问了这个问题。

Q:找到NEAREST MEETING CELL(NMC)

INPUT:与上面相同,+第三行有2个数字,可以找到最接近的会议单元格。

样品输入

23

4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21

9 2(需要在网格/图形中找到相交点9,2)

输出

4

代码:

def main():
testCASES=int(input())
# testCASES=1
for case_number in range(testCASES):
    meshsize=input()

    mesh=input()
    # mesh='4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21'
    det=input()
    # det='9 2'

    mesh=[int(x) for x in mesh.split()]
    det=[int(x) for x in det.split()]

    n1=det[0]
    n2=det[1]

    n1path=[]
    n2path=[]

    for i in range(len(mesh)):
        if not n1path:
            n1path.append(mesh[n1])
        else:
            n1path.append(mesh[n1path[i-1]])

        if not n2path:
            n2path.append(mesh[n2])
        else:
            n2path.append(mesh[n2path[i-1]])

    nearestList=[]
    try:
        for x in n1path:
            nearestList.append(n2path.index(x))
        NEAREST_NODE=n2path[min(nearestList)]
    except Exception as e:
        NEAREST_NODE = -1

    # print(n1path,n2path)
    print(NEAREST_NODE)

main()

工作:

从给定的2个点开始走路径,并通过对最近列表的索引使用min()函数来计算n1path和n2path的第一个公共点。命名是任意的,但这就是核心算法。

它可以处理是否存在循环,并且仍然返回第一个交点。 如果找不到匹配项,则返回-1。

答案 12 :(得分:0)

使用Prims算法在节点中找到最大循环

n = int(input())
v = n
e = v+1
arr = [int(i) for i in input().split()]
graph = [[0 for _ in range(n)] for _ in range(n)]
for i in range(0, len(arr)):
    graph[i][arr[i]] = 1
for i in graph:
    print(i)
    
    
    def min_ind(wieight, visied):
        min_ = -1
        ind = -1
        for i in range(v):
            if(wieight[i] > min_ and visied[i] == False):
                min_ = wieight[i]
                ind = i
        return ind
    
    
    def printPath(parent, i):
        res = []
        while(parent[i] != -1):
            res.append(i)
            i = parent[i]
        res.append(i)
        return res[::-1]
    
    
    # Dijkstra
    visited = [False for _ in range(v)]
    wieight = [0 for _ in range(v)]
    parent = [-1 for i in range(v)]
    wieight[0] = 0
    path = []
    for _ in range(v):
        u = min_ind(wieight, visited)
        if(u == -1):
            continue
        visited[u] = True
        for i in range(v):
            if(graph[u][i] > 0 and visited[i] == False):
                if(wieight[i] < graph[u][i]):
                    wieight[i] = graph[u][i]
                    parent[i] = u
    maximum = 0
    for i in range(0, len(wieight)):
        print("No:", i, " Weight:", wieight[i], " Path:", end=" ")
        path = (printPath(parent, i))
        maximum = max(maximum, len(path))
        print(path, end=" ")
        print()
    print("Longest Cycle: ", maximum)