在将包含无效点和有效点的给定2D矩阵转换为仅包含有效节点的图形时,我遇到了问题。问题是这样的。 我有像
这样的2D矩阵# # # # #
# . C C #
# S # # #
# . . E #
# # # # #
我想找到从S到E的最短距离,记住我必须覆盖所有'C'和'#'作为墙和''。充当自由之路。 现在我想将此矩阵转换为仅包含有效节点的图形。 请帮助我。
n = number of nodes
for i=1 to n: for j=1 to n: d[i][j]=INF
for k=1 to n:
for i=1 to n:
for j=1 to n:
d[i][j] = min(d[i][j], d[i][k] + d[k][j])
shortest = INF
for each permutation a[1],a[2],...a[k] of the 'mustpass' nodes:
shortest = min(shortest, d['start'][a[1]]+d[a[1]][a[2]]+...+d[a[k]]['end'])
print shortest
答案 0 :(得分:3)
字符的2d矩阵是此问题的完美精细图形表示。
每个矩阵元素(i,j)是节点。假设您只能跨越东,西,北,南,则从该节点到其邻居(i +或-1,j +或-1)存在0到4个无向边,这通过简单地测试每个位置中的字符来确定。
您还可以测试i,j值超出范围(负值或太大),但是如果总是存在"墙壁"如图所示,这不是必需的。该墙用作sentinel。
构建一个通用结构来表示嵌入网格中的图形是浪费时间和内存。
答案 1 :(得分:2)
为了制作图表,您必须为每个非墙空间生成一个节点。浏览2D矩阵(假设它只是一个char数组)并创建节点并添加边缘:
nodes = new Node[matrix.length][matrix[0].length]; //instance variable
for ( int row = 0; row < matrix.length; row++ )
{
for ( int col = 0; col < matrix[row].length; col++ )
{
char type = matrix[row][col];
if ( type != '#' )
{
Node n = new Node();
nodes[row][col] = n; //constructor to determine type of node
if ( type == 'S' )
startNode = n;
else if ( type == 'E' )
endNode = n;
findNeighbors(row, col); //assuming nodes and matrix variables are instance variables
}
else
nodes[row][col] = null;
}
}
使用2D节点阵列,您可以通过findNeighbors:
进行添加public void findNeighbors(int row, int col)
{
for ( int r = -1; r <= 1; r++ )
{
for ( int c = -1; c <= 1; c++ )
{
try {
if ( matrix[row+r][col+c] != '#' )
nodes[row][col].addEdge(nodes[row+r][col+c]);
} catch (ArrayIndexOutOfBoundsException e) {}
}
}
}
现在,在所有代码之后,您有一个表示图形的Node对象的2D数组。您可以将Start节点存储在实例变量中,以便方便地引用它并轻松访问其邻居。
使用我编写的代码,Node类需要一个方法addEdge(Node)
,它将参数节点添加到节点列表中。
答案 2 :(得分:1)
这看起来像是一个家庭作业,但为了对话(时间/空间复杂性),我会做一些与众不同的事情。首先,我将创建一个Graph,它只包含可以是路径(例如不是墙)的节点之间的有效边。这最小化了所需的空间。我不会使用矩阵,因为它在实际图形(稀疏)中使用了太多空间,时间复杂度为ROW * COL(方形矩阵的V ^ 2)。
class Graph {
Map<Integer, Set<Integer>> edgeTo;
Graph() {
this.edgeTo = new HashMap<Integer, Set<Integer>>();
}
public int size() {
return edgeTo.size();
}
public void addEdge(int v1, int v2) {
add(v1, v2);
add(v2, v1);
}
private void add(int from, int to) {
if (!edgeTo.containsKey(from)) {
Set<Integer> s = new HashSet<Integer>();
s.add(to);
edgeTo.put(from, s);
} else {
edgeTo.get(from).add(to);
}
}
public Set<Integer> adj(int v) {
return edgeTo.get(v);
}
}
有了这个原位,图表的创建遵循上一篇文章的想法,
private Graph createGrap(char[][] matrix) {
Graph g = new Graph();
for (int r = 0; r < matrix.length; r++) {
for (int c = 0; c < matrix[0].length; c++) {
// skip this cells
if (!isFreeCell(matrix[r][c])) {
continue;
}
int id = createUniqueId(r, c);
if (matrix[r][c] == 'S') {
startVertex = id;
} else if (matrix[r][c] == 'E') {
endVertex = id;
}
createNeighbor(r, c, matrix, g);
}
}
return g;
}
private void createNeighbor(final int r, final int c, final char[][] matrix2, final Graph g) {
for (int row = -1; row <= 1; row++) {
for (int col = -1; col <= 1; col++) {
// avoid the center cell
if (row ==0 && col == 0){
continue;
}
// outside matrix
if ((0 > c + col) || (c + col >= matrix2[0].length) || (0 > r + row) || (r + row >= matrix2.length)) {
continue;
}
char value = matrix2[r+row][c+col];
if (!isFreeCell(value)){
continue;
}
int from = createUniqueId(r, c);
int to = createUniqueId(row+r, col+c);
g.add(from, to);
}
}
}
private boolean isFreeCell(char value) {
return (value != '#' && value !='C');
}
private int createUniqueId(int r, int c) {
return r * MAX_COL + c;
}
现在唯一剩下的就是找到最短路径...使用这个无向图的BFS,没有负的权重边...
private void findSP(Graph g) {
if (g == null || g.size() == 0) {
throw new IllegalArgumentException("empty or null graph");
}
if (g.size() == 1) {
throw new IllegalArgumentException(
"graph's size must be greater than 1");
}
if (startVertex == -1) {
throw new IllegalArgumentException("Start vertex not found");
}
if (endVertex == -1) {
throw new IllegalArgumentException("End vertex not found");
}
Map<Integer, Integer> sonToParent = bfs(g, startVertex, endVertex);
Stack<Integer> path = new Stack<Integer>();
for (int son = endVertex; son!= startVertex; son = sonToParent.get(son)){
path.push(son);
}
path.push(startVertex);
while (!path.isEmpty()){
System.out.print(path.pop() + ", ");
}
}
private Map<Integer, Integer> bfs(Graph g, int startVertex2, int endVertex2) {
Queue<Integer> q = new LinkedList<Integer>();
Set<Integer> marked = new HashSet<Integer>();
Map<Integer, Integer> sonToParent = new HashMap<Integer, Integer>();
q.add(startVertex2);
while (!q.isEmpty()) {
int v = q.poll();
for (Integer s : g.adj(v)) {
if (!marked.contains(s)) {
marked.add(s);
sonToParent.put(s, v);
if (s == endVertex2) {
return sonToParent;
}
q.add(s);
}
}
}
return null;
}
答案 3 :(得分:0)
我要么创建一个节点结构,要么创建一个节点类。例如:
struct Node {
node type; //Indicate in some way if the node is a 'S', '.' or 'E'
std::vector<Node> adjacentNodes;
}
就填充此数据结构而言,我将从'S'块开始。并进行递归调用,如:
Set alreadyVisited;
FillGraph(i,j,Node){
// for all adjacent nodes, add them to Node's adjacentNodes.
// add Node to alreadyVisited Set
// for each of the adjacentNodes (i.e. any neighbor that isn't a wall.
// if(adjacentNode is not in alreadyVisited)
// FillGraph(adjaent-i, adjacent-j, adjacentNode);
}