【Leetcode】1066. Campus Bikes II

该博客介绍了如何运用状态压缩和记忆化DFS解决LeetCode上的'校园自行车分配II'问题,通过优化算法降低工人与自行车之间总曼哈顿距离。文章详细解析了动态规划的状态定义、递归边界条件以及记忆化技巧,同时提供了Java代码实现,代码的时间复杂度为O(mn^2)、空间复杂度为O(mn^2)。
摘要由CSDN通过智能技术生成

题目地址:

https://leetcode.com/problems/campus-bikes-ii/

在平面直角坐标系里,给定 n n n个工人的坐标和 m m m个自行车的坐标( m ≥ n m ≥ n mn并且它们的坐标两两不同),问怎样分配才能使得每个工人都有一个自行车,并且工人与自行车的曼哈顿距离之总和最小。返回那个最小的距离总和。

可以用状态压缩 + 记忆化DFS,用二进制数的二进制位表示每个工人是否已经被分配过了一个自行车。设 f [ k ] [ s ] f[k][s] f[k][s]是还有下标为 k , k + 1 , . . . k,k+1,... k,k+1,...的自行车未分配,且拥车状态为 s s s的情况下,继续分配自行车所带来的曼哈顿距离总和的增加值(即还未被分配车的那些工人贡献的曼哈顿距离之和)。那么题目就是要求 f [ 0 ] [ 0 ] f[0][0] f[0][0]。可以枚举下标为 k k k的车分配给了哪个工人,则遍历 s s s的各个二进制位,看哪个工人还没自行车,分配之,然后进入下一层DFS;当然下标 k k k的车不分配给任何人也是一种选择。关于递归出口,如果待分配的车的数量不够还没车的工人的数量了,则不合法,直接返回 + ∞ +\infty +表示当前得不到合法解;如果每个人都有车了,则返回 0 0 0,因为不需要继续分配车了;如果有人没车,但已经没车分配了,则也返回 + ∞ +\infty +表示当前得不到合法解;有记忆则调取记忆。注意, f [ k ] [ s ] f[k][s] f[k][s]这个值如果不加记忆化的话,是会被多次计算的,因为不同的分配方式很可能导致相同的 s s s值,然而在 k k k s s s都确定的情况下,“至少还增加多少曼哈顿距离”事实上是个定值,不应该被计算多次。代码如下:

import java.util.Arrays;

public class Solution {
    public int assignBikes(int[][] workers, int[][] bikes) {
        int[][] f = new int[bikes.length][1 << workers.length];
        for (int[] row : f) {
            Arrays.fill(row, -1);
        }
        
        return dfs(0, 0, workers, bikes, f);
    }
    
    private int dfs(int pos, int state, int[][] w, int[][] b, int[][] f) {
    	// 车不够分了
        if (b.length - pos < w.length - Integer.bitCount(state)) {
            return 0x3f3f3f3f;
        }
        
        // 每个人都有车了
        if (state == (1 << w.length) - 1) {
            return 0;
        }
        
        // 有人没车,但车已经用完了
        if (pos == b.length) {
            return 0x3f3f3f3f;
        }
        
        // 有记忆则调取记忆
        if (f[pos][state] != -1) {
            return f[pos][state];
        }
        
        int res = 0x3f3f3f3f;
        // 枚举pos这个车分给谁
        for (int i = 0; i < w.length; i++) {
            if ((state >> i & 1) == 0) {
                res = Math.min(res, dis(w, b, i, pos) + dfs(pos + 1, state | (1 << i), w, b, f));
            }
        }
        
        // 枚举pos这个车浪费掉的情况
        res = Math.min(res, dfs(pos + 1, state, w, b, f));
        // 回溯前做记忆
        f[pos][state] = res;
        return res;
    }
    
    private int dis(int[][] w, int[][] b, int i, int j) {
        return Math.abs(w[i][0] - b[j][0]) + Math.abs(w[i][1] - b[j][1]);
    }
}

时间复杂度 O ( m n 2 n ) O(mn2^n) O(mn2n),空间 O ( m 2 n ) O(m2^n) O(m2n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值