启发性搜索算法

启发性搜索

在宽度优先和深度优先搜索里面,我们都是根据搜索的顺序依次进行搜索,可以称为盲目搜索,搜索效率非常低。

在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。

Leetcode 1723

1723. 完成所有工作的最短时间

给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。

请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。

返回分配方案中尽可能 最小 的 最大工作时间 。

参考宫水三叶的解法,记录一下:

class Solution {
    int[] jobs;
    int n, k;
    int ans = 0x3f3f3f3f;//无穷大,并且不像Integer.MAX_VALUE加1直接溢出
    public int minimumTimeRequired(int[] _jobs, int _k) {
        jobs = _jobs;
        n = jobs.length;
        k = _k;
        int[] sum = new int[k];
        dfs(0, 0, sum, 0);
        return ans;
    }
    /**
     * u     : 当前处理到那个 job
     * used  : 当前分配给了多少个工人了
     * sum   : 工人的分配情况          例如:sum[0] = x 代表 0 号工人工作量为 x
     * max   : 当前的「最大工作时间」
     */
    void dfs(int u, int used, int[] sum, int max) {
        if (max >= ans) return;
        if (u == n) {
            ans = max;
            return;
        }
        // 优先分配给「空闲工人」
        if (used < k) {
            sum[used] = jobs[u];
            dfs(u + 1, used + 1, sum, Math.max(sum[used], max));
            sum[used] = 0;
        }
        //i < used是精华!
        for (int i = 0; i < used; i++) {
            sum[i] += jobs[u];
            dfs(u + 1, used, sum, Math.max(sum[i], max));
            sum[i] -= jobs[u];
        }
    }
}

为什么是 i < used

      if (used < k) {
            sum[used] = jobs[u];
            dfs(u + 1, used + 1, sum, Math.max(sum[used], max));
            sum[used] = 0;
        }
        for (int i = 0; i < used; i++) {
            sum[i] += jobs[u];
            dfs(u + 1, used, sum, Math.max(sum[i], max));
            sum[i] -= jobs[u];
        }

当if语句执行完时,意味着平均分配时的解已经找完了。

现在尝试寻找不平均分配时的解。 也就是尝试有空余人不分配任务,也就是有部分人先执行两个 / 多个任务而有部分人空着。也就是搜索树的剩余部分。

例:1 2 4分配给甲乙,if执行完之后,已经尝试了平均分配的情况: 即先分配1给甲,随后分配2给乙(剩余的空闲的人),再搜索4。或其他情况。

if完成之后,现在平均分配方案已经全部尝试完毕,所以我们应该:尝试先将1分配给甲,再将2分配给甲,再搜索4。

如果有多个人,会依次在 sum[used] = 0位置时回溯多次,依次在每一次回溯时回退多个空余的人,继续搜索树。

注意:例:有三个人甲乙丙,if执行完成。 第一轮:退出dfs(used+1)语句, sum[used] = 0语句回退空余的丙。进入for(i < used),对下一个任务在甲乙之间分配(注意此时used为2,区间为甲乙,和dfs(used+1)的参数不同)。然后进入dfs,这时再进if,给空余的丙分配任务。

第二轮,退出dfs,回退空余的乙丙,进入for(i < used),对下一个任务在甲之间分配。然后进入dfs,这时再进if,给空余的乙丙分配任务。再次进入回溯步骤。

如果是i < k,多了[used,k),在这部分空闲工人中分配的结果和if(used < k)中的结果是一样的,都是分配给空闲的工人,因为他本身就没有「工作时间」,给谁都是一样的。


   转载规则


《启发性搜索算法》 锦泉 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录