次の問題を解決しようとしていますが、アルゴリズムやアプローチを開発できませんでした。私は数時間調査し、問題を「最短パス」グラフ/行列問題または動的プログラミング問題にマッピングしようとしましたが、失敗しました。
幅がw、高さがhのグリッドを指定します。グリッドの各セルは潜在的な建物の区画を表し、このグリッド内に「n」個の建物を追加します。目標は、すべての区画の中で最も遠い建物をできるだけ建物に近づけることです。ロットに配置する建物の数である入力nが与えられた場合、最も離れた空のロットが建物から離れる距離を最小化するように建物の配置を決定します。移動は水平および垂直に制限されています。つまり、斜めの移動は必要ありません。
例えば、 w=4, h=4 and n=3
。最適なグリッド配置により、建物から2単位の距離内に任意のロットが設定されます。この場合の答えは2です。
「0」は建物の最適な配置を示し、この場合、各セルの最も近い建物までのすべての最短距離の最大値は「2」です。
1 0 1 2
2 1 2 1
1 0 1 0
2 1 2 1
上記は1つの最適なソリューションを表しています。上記の配列を例として回転させると、より多くの可能性があります。上記は3つの建物(n = 3)のうち、1つの建物がインデックス(0,1)に、2番目の建物が(2,1)に、3番目の建物が(2,3)に配置されているため、最適なソリューションです。周囲の水平および垂直距離は、水平および/または垂直に移動するたびに1を加算することにより、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
2つの建物(ゼロ)を最適に配置する必要があります。この場合の最適な計画の1つは次のとおりです。
01
11
10
0 → 1
↓
1 1
↑
1 ← 0
Answer: 1
例として、この場合、次のプランは1ではなく2の最大最小距離を持っているため、最適ではありません。したがって、0が3つの「1上記の最適なシナリオのように、この場合は2つではなく位置。
1 → 2
↑
0 → 1
↓ ↑
1 ← 0
例2)
w=5, h=1, n=1
1つの建物(ゼロ)を最適に配置する必要があります。最適な計画の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
正直なところ、解決策を推測することができなかったため、コードを記述できませんでした。
2つの与えられた点が2Dマトリックスの固定点である場合、2つの間の距離または最短距離を見つけることができます。しかし、この場合、2つのポイントがどこにあるかわかりませんか?多くの最適なソリューションがあり、各場所に0の組み合わせを配置し、最も遠い距離を見つけることは不可能であり、実行不可能です。最大量が1になる位置(中間またはw/2など)に配置しようとしましたが、うまくいかないようです。既存のアルゴリズムをこの問題に適用できますか?
指定された制約に従って、行列サイズ(w * h)は28を超えることはできません。これはかなり小さい数です。また、nの最大可能値は5です。組み合わせ論の知識が少ないことから、 28C5 最悪の場合に特定のグリッドから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;
}
}
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);
}
}
Pythonを使用してこの質問を解決しようとしました。答えの中核はステップ関数にあります。ステップ関数は、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)