繁体   English   中英

矩阵中最短距离的最大值

[英]Maximal value among shortest distances in a matrix

我正在尝试解决以下问题,但无法开发算法或方法。 我研究了几个小时并试图将问题映射到“最短路径”图/矩阵问题或动态规划问题,但没有成功。

给定一个宽度为 w、高度为 h 的网格。 网格的每个单元格代表一个潜在的建筑物,我们将在此网格内添加“n”个建筑物。 目标是使所有地块中最远的地块尽可能靠近建筑物。 给定输入 n,即要放置在地块中的建筑物数量,确定建筑物放置以最小化最远的空地块与建筑物的距离。 运动仅限于水平和垂直,即不需要对角运动。

例如, w=4, h=4 and n=3 最佳的网格放置将任何地段设置在建筑物的两个单位距离内。 这个案例的答案是2。

“0”表示最佳建筑物放置,在这种情况下,每个单元格到最近建筑物的所有最短距离的最大值为“2”。

1 0 1 2
2 1 2 1
1 0 1 0
2 1 2 1

以上代表了一种最优解,可能还有更多像上面旋转的阵列的例子。 以上是最佳解决方案,因为在 3 个建筑物 (n=3) 中,一个建筑物位于索引 (0,1),第二个位于 (2,1),第三个位于 (2,3)。 每次我们水平和/或垂直移动时,周围的水平和垂直距离都显示为 1 和 2。 再次注意,不允许对角移动:

1 ← 0 → 1 → 2
    ↓
2 ← 1 → 2 ← 1
    ↑       ↑
1 ← 0 → 1 ← 0
    ↓       ↓
2 ← 1 → 2 ← 1

其他例子:

示例 1)

w=3, h=3, n=2

必须最佳放置两座建筑物(零点)。 这种情况下的最佳计划之一是:

01
11
10

0 → 1
↓
1   1
    ↑  
1 ← 0

Answer: 1

例如,下面的计划在这种情况下不是最优的,因为它的最大最小距离为 2 而不是 1。因此,即使 0 覆盖三个“1”,将 0 贪婪地放置在索引 (1,0) 上也不起作用" 在这种情况下的位置,而不是上述最佳方案中的两个位置。

1 → 2
↑
0 → 1
↓   ↑   
1 ← 0

例 2)

w=5, h=1, n=1

必须最佳放置一栋建筑物(零)。 最优方案之一:

2 ← 1 ← 0 → 1 → 2

Answer: 2

上述场景中的非最优计划示例:

3 ← 2 ← 1 ← 0 → 1

应完成以下功能:

int findMinDist(int w, int h, int n)
{

}

约束:

1<=w,h
w*h <=28
1<=n<=5
n<=w*h

我一直无法编写任何代码,因为老实说我无法推断出解决方案。

如果给定的两个点是二维矩阵中的固定点,我可以找到两者之间的距离或最短距离。 但是,在这种情况下,我不知道这两个点会在哪里? 可以有很多最优解,在每个地方放置0的组合,找到最远的距离是不可能的,也不可行。 我试图将它们放在最大数量为 1 的位置(如中间或 w/2),但这似乎也不起作用。 可以将现有算法应用于此问题吗?

根据给定的约束,矩阵大小 ( w*h ) 不能超过 28,这是一个相当小的数字。 此外, n的最大可能值是 5。根据对组合学的一些了解,我们知道在最坏的情况下,有28 C 5种方法可以从给定的网格中选择 5 个批次。 该数字评估为 98280,这是一个足够小的空间,可以通过记忆进行搜索。 由于w*h的最大值是 28,我们可以用一个整数位掩码来表示整个网格,它与剩余的建筑地块数量将形成我们 DP 的状态。 为了计算最终状态的最远剩余地段,我们使用广度优先搜索 (BFS),通过使用我们已经建立建筑物的所有点初始化队列。 共享运行速度足够快的相同代码https://ideone.com/ix1nh8

int W, H, N;

int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, -1, 1};

int calc(int i, int j) {
    if(W <= H)
        return  i + W * j;
    return H * i + j;
}

bool get(int bitmask, int i, int j) {
    return (bitmask&(1<<calc(i,j)));
}

int bfs(int bitmask) {
    int dist[W][H];
    memset(dist, -1, sizeof dist);

    int maxDist = 0;
    queue<pair<int,int>> Q;

    for(int i = 0; i < W; i++)
        for(int j = 0; j < H; j++)
            if(get(bitmask, i, j)) {
                dist[i][j] = 0;
                Q.push({i, j});
            }
    assert(Q.size() == N);

    while(!Q.empty()) {
        int x = Q.front().first;
        int y = Q.front().second;
        maxDist = max(maxDist, dist[x][y]);
        Q.pop();

        for(int d = 0; d < 4; d++) {
            int newx = x + dx[d];
            int newy = y + dy[d];

            if(newx >= W || newy >= H || newx < 0 || newy < 0)
                continue;
            if(dist[newx][newy] == -1) {
                dist[newx][newy] = dist[x][y] + 1;
                Q.push({newx, newy});
            }
        }
    }
    return maxDist;
}

map<pair<int,int>, int> dp;

int solve(int bitmask, int left) {
    if(left == 0) {
        return bfs(bitmask);
    }
    if(dp.find({bitmask, left}) != dp.end()) {
        return dp[{bitmask, left}];
    }
    int minDistance = INT_MAX;
    for(int i = 0; i < W; i++)
        for(int j = 0; j < H; j++)
            if(!get(bitmask, i, j)) {
                int val = solve((bitmask|(1<<calc(i, j))), left-1);
                minDistance = min(minDistance, val);
            }
    return dp[{bitmask, left}] = minDistance;
}

没有位掩码且需要通过将位置传递给递归调用来记忆的 Java 解决方案

class MaximumShortestDist
{
    static int[] dx = new int[]{1, -1, 0, 0};
    static int[] dy = new int[]{0, 0, -1, 1};

    public static void main(String[] args) {
        System.out.println(findMinDist(14,2,5));
    }

    static int findMinDist(int w, int h, int n)
    {

        int[][] grid = new int[w][h];
        for(int i=0;i<w;i++)
            Arrays.fill(grid[i],-1);
        return solve(n,w,h,0,0,grid);
    }

    static int bfs(int W, int H, int[][] grid) {

        int[][] dist = new int[W][H];
        for(int i=0;i<W;i++)
            for(int j=0;j<H;j++)
                dist[i][j] = grid[i][j];

        int maxDist = 0;
        Queue<Location> Q = new LinkedList<>();
        for(int i = 0; i < W; i++)
            for(int j = 0; j < H; j++)
                if(dist[i][j] == 0){
                    Q.add(new Location(i,j));
                }

        while(!Q.isEmpty()) {
            int x = Q.peek().first;
            int y = Q.peek().second;
            maxDist = Math.max(maxDist, dist[x][y]);
            Q.poll();

            for(int d = 0; d < 4; d++) {
                int newx = x + dx[d];
                int newy = y + dy[d];

                if(newx >= W || newy >= H || newx < 0 || newy < 0)
                    continue;
                if(dist[newx][newy] == -1) {
                    dist[newx][newy] = dist[x][y] + 1;
                    Q.add(new Location(newx, newy));
                }
            }
        }
        return maxDist;
    }

    static int solve(int left, int W, int H, int row, int col,int[][] grid) {
        if(left == 0) {
            return bfs(W,H,grid);
        }
        int r = row,c=col;
        if(col >= H) {
            r += col/H;
            c = col%H;
        }
        int minDistance = Integer.MAX_VALUE;
        for(int i=r;i<W;i++){
            for(int j=c;j<H;j++) {
                //Mark Building locations in the recursive call.
                grid[i][j] = 0;
                int val = solve(left-1, W, H,i,j+1,grid);
                minDistance = Math.min(minDistance, val);
                // Remove the building
                grid[i][j] = -1;
            }
        }
        return minDistance;
    }
}


class Location {
    int first;
    int second;
    Location(int x, int y) {
        first = x;
        second = y;
    }
}

看看这个简单的python程序。

  1. 它找到了办公室所在位置的所有可能排列
  2. 然后它找到离最近办公室的单元格的最大距离[使用 BFS]
  3. 取所有这些最大值的最小距离
def findMinDistance(w, h, n):
    def maxDist(w,h,p):
        arr = [[-1]*h for i in range(w)]
        q = collections.deque()
        for x in p:
            r,c = x//h, x%h
            arr[r][c] = 0
            q.append((r,c,0))

        maxd = -1
        while len(q):
            x,y,d = q.popleft()
            maxd = max(maxd, d)
            if 0 <= x+1 < w and arr[x+1][y] == -1:
                arr[x+1][y] = d+1
                q.append((x+1,y,d+1))
            if 0 <= x-1 < w and arr[x-1][y] == -1:
                arr[x-1][y] = d+1
                q.append((x-1,y,d+1))
            if 0 <= y+1 < h and arr[x][y+1] == -1:
                arr[x][y+1] = d+1
                q.append((x,y+1,d+1))
            if 0 <= y-1 < h and arr[x][y-1] == -1:
                arr[x][y-1] = d+1
                q.append((x,y-1,d+1))

        return maxd

    ans = 100000
    pos = [i for i in range(w*h)]
    for p in list(itertools.combinations(pos, n)):
        ans = min(ans, maxDist(w,h,p))
    return ans

没有按位运算的 Java 代码。

import javafx.util.Pair;
import java.util.*;

class Office_N {
    // W for width, H for height, N for no of offices to build
    int W, H, N;

    // dx and dy value together gives (x,y)
    // which helps to move in 4 adjacent cells
    // Right (1,0)
    // Left (-1,0)
    // Down (0,1)
    // Up (0,-1)
    int[] dx = {1, -1, 0, 0};
    int[] dy = {0, 0, 1, -1};
    Map<String, Integer> dp = new HashMap<>();

    int[][] grid;

    // Constructor will set the values and clear the hashmap.
    public Office_N(int w, int h, int n) {
        W = w;
        H = h;
        N = n;
        dp.clear();
        grid = new int[W][H];

        for (int[] r : grid) {
            Arrays.fill(r, 0);
        }
    }

    // We convert the 2D array of W*H into 1D array in Row Order (if square matrix or Width is less),
    // or Column Wise (if Height is less)
    // BitMask holds the bits 0 empty spots, and 1 for where offices are present
    // Left means how many offices are still left to be built
    public int solve(int[][] grid, int left) {

        // If no more offices are left to be built, get the maximum distance for this scenario
        if (left == 0) {
            return bfs(grid);
        }

        StringBuilder k = new StringBuilder();

        for (int i = 0; i < W; i++) {
            for (int j = 0; j < H; j++) {
                if (grid[i][j] == 1) {
                    k.append(i + ":" + j + "::");
                }
            }
        }

        k.append("#" + left);
        // if the current scenario along with offices left are already processed, return the result
        String key = k.toString();
        if (dp.containsKey(key)) {
            return dp.get(key);
        }

        int[][] gridtemp = new int[W][H];
        for (int i = 0; i < W; i++) {
            for (int j = 0; j < H; j++) {
                gridtemp[i][j] = grid[i][j];
            }
        }

        //  We are trying every possible scenario to build offices in the given grid
        int minDist = Integer.MAX_VALUE;
        for (int i = 0; i < W; i++) {
            for (int j = 0; j < H; j++) {
                // If no office present in (i,j)th location, put one office there and check the minimum distance for that scenario
                if (gridtemp[i][j] == 0) {
                    gridtemp[i][j] = 1;
                    int val = solve(gridtemp, left - 1);
                    minDist = Math.min(minDist, val);
                    gridtemp[i][j] = 0;
                }
            }
        }

        // Store the min distance possible for the current scenario
        dp.put(key, minDist);
        return minDist;
    }

    // This function gives the maximum distance from all the empty spots to the offices for a given case of scenario
    private int bfs(int[][] grid) {
        // get a distance matrix with initial values as -1
        int[][] dist = new int[W][H];
        for (int[] row : dist)
            Arrays.fill(row, -1);

        int maxDist = 0;
        // Queue for processing the cells in Bredth-First-Search order.
        Queue<Pair<Integer, Integer>> Q = new LinkedList<>();

        // if office is present at (i,j)th location, the distance is 0, and put the (i,j) pair in Queue
        for (int i = 0; i < W; i++) {
            for (int j = 0; j < H; j++) {
                if (grid[i][j] == 1) {
                    dist[i][j] = 0;
                    Q.add(new Pair<>(i, j));
                }
            }
        }


        while (!Q.isEmpty()) {
            Pair<Integer, Integer> kv = Q.poll();
            int x = kv.getKey();
            int y = kv.getValue();

            // Get maximum distance for (i,j)th location
            maxDist = Math.max(maxDist, dist[x][y]);

            // Process all adjacent cells
            for (int d = 0; d < dx.length; d++) {
                int xNew = x + dx[d];
                int yNew = y + dy[d];

                // if the adjacent cell is within grid boundary, and is not yet processed,
                // set the max dist of he adjacent cell 1 more than the (i,j)th cell
                // add the adjacent cell to queue
                if (xNew >= 0 && xNew < W && yNew >= 0 && yNew < H && dist[xNew][yNew] == -1) {
                    dist[xNew][yNew] = dist[x][y] + 1;
                    Q.add(new Pair<>(xNew, yNew));
                }
            }
        }

        return maxDist;
    }

    public static void main(String[] args) {
        Office_N ofc = new Office_N(4, 4, 3);
        int res = ofc.solve(ofc.grid, ofc.N);
        System.out.println(res);
    }
}

我使用数学方法来实现解决方案。

最短距离来自网格中最佳占用分区 这意味着每个分区的差异的最大值为1 w=4, h=4 and n=3为例。 在这个网格中,内部有 16 个节点。 对于建筑物的每个占用面积,我们可以通过w*h/n = 16/3 = 5.33... 这意味着该区域的最大节点数是天花板数A = Math.ceil(w*h/n) = 6

由上面的计算,每个分区只有一个节点,所以我们可以假设这个建筑物位于中心。 然后我们可以计算出从建筑物到边缘的最长距离是Math.sqrt(A) = 2.45 上限数为3 由于它是从零开始的,我们可以减去 1 得到2

特殊情况- 宽度或高度为1

我们可以使用比上面简单得多的其他数学计算来解决。

这是我的 C++ 版本的解决方案。 我已经测试了Build Offices Google Doc 中的所有测试用例,并且可以正常工作。 挑战似乎非常可怕; 然而,如果你发现w*h <= 28描述有问题,它就会变成一个非常简单的问题。 因此,我只需要使用深度优先搜索列出所有0和广度优先搜索的情况来找到访问整个地图的最短路径,完成:)你可以在评论中找到更多细节,祝你好运。

#include <bits/stdc++.h>
using namespace std;

int w, h, n;
vector<int> dirx = {0, 0, 1, -1};
vector<int> diry = {1, -1, 0, 0};
int maxdistance = INT_MAX;

// help convert (x, y) to decimal-based position
int calc(int x, int y) {
    return (w * x) + y;
}

// check whether out of bound and visited before in bfs
bool check_valid(int x, int y, unordered_set<int> visited) {
    if(x < 0 || x >= h)
        return false;
    if(y < 0 || y >= w)
        return false;
    if(visited.count(calc(x, y)))
        return false;
    return true;
}

// level-ordered bfs and each time find the longest path on map
int bfs(vector<vector<int>> map,
        vector<pair<int, int>> start_point) {
    int local_max = 0;
    queue<pair<int, int>> q;
    unordered_set<int> visited;

    for(auto s : start_point) {
        q.push(s);
        visited.insert(calc(s.first, s.second));
    }

    int dist = 0;
    while(!q.empty()) {
        int sz = q.size();
        dist++;
        for(int i = 0; i < sz; i++) {
            pair<int, int> head = q.front();
            q.pop();
            for(int i = 0; i < 4; i++) {
                pair<int, int> neighbor = make_pair(head.first + dirx[i], head.second + diry[i]);
                if(check_valid(neighbor.first, neighbor.second, visited)) {
                    map[neighbor.first][neighbor.second] = dist;
                    visited.insert(calc(neighbor.first, neighbor.second));
                    q.push(neighbor);
                }
            }
        }
    }

    // after visiting the whole map
    // find the maxdistance
    for(int i = 0; i < h; i++) {
        for(int j = 0; j < w; j++) {
            if(map[i][j] > local_max)
                local_max = map[i][j];
        }
    }

    return local_max;
}

// list all the cases for starting point: 0
// in fact, not all the cases
// I considered optimal start point would show up in each distance of (w*h/n)
void dfs(vector<vector<int>> map,
        int d,
        int n_,
        int n, 
        vector<pair<int, int>> start_point) {
    if(n_ == n) {
        // cout << "Map: " << '\n';
        /**
        for(int i = 0; i < map.size(); i++) {
            for(int j = 0; j < map[i].size(); j++) {
                cout << map[i][j] << ' ';
            }
            cout << '\n';
        }
        cout << "distance?: ";**/
        if(bfs(map, start_point) < maxdistance) {
            maxdistance = bfs(map, start_point);
            cout << maxdistance << '\n';
        }
        return;
    }

    for(int i = 0; i < d; ++i) {
        int pos = n_ * d + i;
        map[pos / w][pos % w] = 0;
        start_point[n_].first = pos / w;
        start_point[n_].second = pos % w;
        dfs(map, d, n_ + 1, n, start_point);
        map[pos / w][pos % w] = INT_MAX;
        start_point[n_].first = -1;
        start_point[n_].second = -1;
    }
}

int main() {
    cin >> w >> h >> n;
    int d = (w*h)/n;   // build one office in each d distance
    vector<vector<int>> map(h, vector<int>(w, INT_MAX));
    vector<pair<int, int>> start_point(n, make_pair(-1, -1));
    dfs(map, d, 0, n, start_point);
    cout << "Max distance of one of optimal solution is: " << maxdistance << '\n';

    return 0;
}

我尝试使用 python 解决这个问题。 答案的核心在于我的 step 函数,它获取 W x H 网格中 N 建筑物的所有可能位置,并将结果作为列表给出。 列表中的每个位置都是 W*i + H 处的位置。 即 2x2 中的位置被视为 0、1、2、3。



# generator function to give each building position 
# in a W x H grid
def step(W, H, N):
    dim = W * H
    slots = [n for n in range(N)]
    slots_temp = list(slots)
    persist = list(slots)
    last = [dim - n for n in slots]
    last = last[::-1]
    while slots != [0] * N:
        yield slots
        for i in range(len(slots)-1,-1,-1):
            slots[i]+=1
            if slots[i] >= last[i] :
                slots[i] = 0
            else:
                while i < len(slots)-1:
                    slots[i+1] = slots[i] + 1
                    i+=1
                break

# converts a ixj to a step
# assumes W <= H
def get_step(i, j, W , H):
    return (i * W) + j

# does bfs from each building position
# and gets the maximum distance 
def bfs(step,W,H):
    dist = [[-1]*H for i in range(W)]
    queue = []
    dx = [1,-1,0,0]
    dy = [0,0,1,-1]
    for i in range(W):
        for j in range(H):
            step_val = get_step(i, j, W, H)
            if step_val in step:
                dist[i][j] = 0
                queue.append((i,j))
    max_val = 0
    while len(queue) != 0:
        i,j = queue.pop(0)
        max_val = max(max_val, dist[i][j])
        for _dx,_dy in zip(dx,dy):
            new_i,new_j = i + _dx, j + _dy
            if new_i < 0 or new_i >= W or new_j <0 or new_j >= H:
                continue
            if dist[new_i][new_j] == -1:
                dist[new_i][new_j] = dist[i][j] + 1
                queue.append((new_i,new_j))
    return max_val


# calls each posible position of the building
# and computes the minimum distance of all
def main(W, H, N ): 
    min_val = float('inf')
    if W > H:
        W, H = H, W
    s = step(W, H, N)
    for slot in s:
        b = bfs(slot, W, H)
        min_val = min(min_val, b)
    return min_val



main(4, 4, 2)

用于此的 Ruby 代码

class MaximumShortestDist
  def findMinDist(w, h, n)
  grid = Array.new(w){Array.new(h)}
  for i in 0...w do
      grid[i].fill(-1)
  end

  solve(n,w,h,0,0,grid);
end

def bfs(ww, hh, grid)
        dx = [1, -1, 0, 0];
dy = [0, 0, -1, 1];
    dist = Array.new(ww){Array.new(hh)};

    for i in 0...ww do
        for j in 0...hh do
            dist[i][j] = grid[i][j];
          end
        end

    maxDist = 0;

    qu = Array.new

    for i in 0...ww do
      for j in 0...hh do
          if dist[i][j] == 0
             qu.push(Location.new(i,j));
          end
      end
    end

    while !qu.empty?
        x = qu.first.first;
        y = qu.first.second;
        maxDist = [maxDist, dist[x][y]].max;
        qu.shift;

        for d in 0...4  do
            newx = x + dx[d];
            newy = y + dy[d];

            next if newx >= ww || newy >= hh || newx < 0 || newy < 0


            if dist[newx][newy] == -1
                dist[newx][newy] = dist[x][y] + 1;
                qu.push(Location.new(newx, newy));
            end
        end
    end

    return maxDist;
end

def solve(left, ww, hh, row,  col, grid)
    return bfs(ww,hh,grid) if left == 0

    r = row
    c=col

    if col >= hh
        r += col/hh;
        c = col%hh;
    end

    minDistance = 999;
    for i in r...ww do
        for j in c...hh  do
            grid[i][j] = 0;
            val = solve(left-1, ww, hh,i,j+1,grid);
            minDistance = [minDistance, val].min;
            grid[i][j] = -1;
        end
    end
    return minDistance;
  end
end


class Location
   attr_reader :first, :second
  def initialize(x, y)
    @first = x;
    @second = y;
  end
end

puts MaximumShortestDist.new.findMinDist(2,3,2)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM