【算法设计与分析(课后答案)】回溯

1. 求解会议安排问题

【问题描述】陈老师是一个比赛队的主教练,有一天,他想给团队成员开会,应该为这次会 议安排教室,但教室缺乏,所以教室管理员必须通过接受订单和拒绝订单优化教室的利用率。 如果接受一个订单,则该订单 的开始时间和结束时间成为一个活动。注意,每个时间段只 能安排一个订单。请找出一个最大化的总活动时间的方法。你的任务是这样的:读入订单, 计算所有活动(接受的订单)占用时间的最大值。

【输入描述】标准等的输入将包含多个测试用例。对于每个测试用例,第 1 行是一个整数 n(n<=10 000),接着的 n 行中每一行包括两个整数 p 和 k(1<=p<=k<=300 000),其中 p 是一 个订单的开始时间,k 是结束时间。
【输出描述】对于每个测试用例,输出所有活动占用时间的最大值。

【输入样例】4
         1 2
         3 5
         1 4
         4 5
【输出样例】4

public class Solution1 {

    private int maxMeetingTime(int[][] time) {
        traceBack(0, time, 1, new boolean[time.length]);
        return maxSum;
    }

    private int maxSum;

    private void traceBack(int tempSum, int[][] time, int available, boolean[] used) {
        maxSum = Math.max(maxSum, tempSum);
        for (int i = 0; i < time.length; i++) {
            if (!used[i] && time[i][0] >= available) {
                used[i] = true;
                traceBack(tempSum + time[i][1] - time[i][0], time, time[i][1], used);
                used[i] = false;
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int len = scanner.nextInt();
        int[][] time = new int[len][2];
        for (int i = 0; i < len; i++) {
            time[i][0] = scanner.nextInt();
            time[i][1] = scanner.nextInt();
        }
        System.out.println(new Solution1().maxMeetingTime(time));
    }
}

 


2. 求解最小机器重量设计问题Ⅰ

【问题描述】 某一机器由n个部件组成,编号1–n, 每一种部件都可以由m个供货商提供,供货商编号1–m。设wij表示供应商j处购得的部件i的重量,cij是相应的价格。对于给定的机器部件重量和机器部件价格,计算总价格不超过cost的最小重量机器设计,可以在同一个供应商处购得多个部件。

【输入描述】第一行由3个正整数n,m,d。接下来的2n行,每行m个数。前n行是c,后n行是w。
【输出描述】输出的第一行包括n个整数,表示每个对应的供应商的编号,第二行为对应的重量。

【输入样例】3 3 7
         1 2 3
         3 2 1
         2 3 2
         1 2 3
         5 4 2
         2 1 2
【输出样例】1 3 1
         4

public class Solution2 {

    private void minMachineWeight(int[][] weight, int[][] cost, int maxCost) {
        traceBack(new ArrayList<>(), 0, 0, 0, weight, cost, maxCost);
        for (int n : resList) {
            System.out.print(n + " ");
        }
        System.out.println();
        System.out.println(minWeight);
    }

    // 零件最小重量
    private int minWeight = Integer.MAX_VALUE;
    // 采购零件的工厂
    private List<Integer> resList = new ArrayList<>();

    private void traceBack(List<Integer> tempList, int totalWeight, int totalCost, int index, int[][] weight, int[][] cost, int maxCost) {
        if (totalCost > maxCost || totalCost > minWeight) {
            return;
        }
        if (index == weight.length) {
            if (totalWeight < minWeight) {
                minWeight = totalWeight;
                resList = new ArrayList<>(tempList);
            }
            return;
        }
        for (int i = index; i < weight.length; i++) {
            for (int j = 0; j < weight[0].length; j++) {
                tempList.add(j + 1);
                traceBack(tempList, totalWeight + weight[i][j], totalCost + cost[i][j], index + 1, weight, cost, maxCost);
                tempList.remove(tempList.size() - 1);
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[][] weight = new int[n][m];
        int[][] cost = new int[n][m];
        int maxCost = scanner.nextInt();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                weight[i][j] = scanner.nextInt();
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cost[i][j] = scanner.nextInt();
            }
        }
        new Solution2().minMachineWeight(weight, cost, maxCost);
    }
}

 


3. 求解最小机器重量设计问题Ⅱ

【问题描述】 某一机器由n个部件组成,编号1–n, 每一种部件都可以由m个供货商提供,供货商编号1–m。设wij表示供应商j处购得的部件i的重量,cij是相应的价格。对于给定的机器部件重量和机器部件价格,计算总价格不超过cost的最小重量机器设计,在同一个供应商处最多只能购得一个部件。

【输入描述】第一行由3个正整数n,m,d。接下来的2n行,每行m个数。前n行是c,后n行是w。
【输出描述】输出的第一行包括n个整数,表示每个对应的供应商的编号,第二行为对应的重量。

【输入样例】3 3 7
         1 2 3
         3 2 1
         2 3 2
         1 2 3
         5 4 2
         2 1 2
【输出样例】1 2 3
         5

public class Solution3 {

    private void minMachineWeight(int[][] weight, int[][] cost, int maxCost) {
        traceBack(new ArrayList<>(), 0, 0, 0, weight, cost, maxCost, new boolean[weight[0].length]);
        for (int n : resList) {
            System.out.print(n + " ");
        }
        System.out.println();
        System.out.println(minWeight);
    }

    // 零件最小重量
    private int minWeight = Integer.MAX_VALUE;
    // 采购零件的工厂
    private List<Integer> resList = new ArrayList<>();

    private void traceBack(List<Integer> tempList, int totalWeight, int totalCost, int index, int[][] weight, int[][] cost, int maxCost, boolean used[]) {
        if (totalCost > maxCost || totalCost > minWeight) {
            return;
        }
        if (index == weight.length) {
            if (totalWeight < minWeight) {
                minWeight = totalWeight;
                resList = new ArrayList<>(tempList);
            }
            return;
        }
        for (int i = index; i < weight.length; i++) {
            for (int j = 0; j < weight[0].length; j++) {
                if (!used[j]) {
                    used[j] = true;
                    tempList.add(j + 1);
                    traceBack(tempList, totalWeight + weight[i][j], totalCost + cost[i][j], index + 1, weight, cost, maxCost, used);
                    tempList.remove(tempList.size() - 1);
                    used[j] = false;
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[][] weight = new int[n][m];
        int[][] cost = new int[n][m];
        int maxCost = scanner.nextInt();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                weight[i][j] = scanner.nextInt();
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                cost[i][j] = scanner.nextInt();
            }
        }
        new Solution3().minMachineWeight(weight, cost, maxCost);
    }
}

 


4. 求解密码问题

【问题描述】给定一个整数n和一个由不同大写字母组成的字符串str(长度大于5、小于12),每一个字母在字母表中对应有一个序数(A=1,B=2,…,Z=26),从str中选择5个字母构成密码,例如选取的5个字母为v、w、x、y和z,他们要满足v的序号-(w的序数)2+(x的序数)3-(y的序数)4+(z的序数)5=n。例如,给定的n=1、字符串str为"ABCDEFGHIJKL",一个可能的解是“FIECB”,因为6-92+53-34+25=1,但这样的解可能有多个,最终结果是按字典序最大的那个,所以这里的正确答案是“LKEBA”。

【输入描述】每一行为n和str,输入0结束。
【输出描述】每一行对应相应密码,没有解就输出no solution。

【输入样例】1 ABCDEFGHIJKL
         11700519 ZAYEXIWOVU
         3072997 SOUGHT
         1234567 THEQUICKFROG
         0
【输出样例】LKEBA
         YOXUZ
         GHOST
         no solution

public class Solution4 {

    private String findPassword(String s, int target) {
        int len = s.length();
        // 转化为整型数组
        Integer[] letters = new Integer[len];
        for (int i = 0; i < len; i++) {
            letters[i] = s.charAt(i) - 'A' + 1;
        }
        // 降序排序
        Arrays.sort(letters, (o1, o2) -> o2 - o1);
        // 回溯
        int v, w, x, y, z;
        boolean[] used = new boolean[27];
        for (int i = 0; i < len; i++) {
            if (used[v = letters[i]]) continue;
            used[v] = true;
            for (int j = 0; j < len; j++) {
                if (used[w = letters[j]]) continue;
                used[w] = true;
                for (int k = 0; k < len; k++) {
                    if (used[x = letters[k]]) continue;
                    used[x] = true;
                    for (int m = 0; m < len; m++) {
                        if (used[y = letters[m]]) continue;
                        used[y] = true;
                        for (int n = 0; n < len; n++) {
                            if (used[z = letters[n]]) continue;
                            used[z] = true;
                            if (Math.pow(v, 1) - Math.pow(w, 2) + Math.pow(x, 3) - Math.pow(y, 4) + Math.pow(z, 5) == target) {
                                StringBuilder builder = new StringBuilder();
                                builder.append((char)('A' + v - 1));
                                builder.append((char)('A' + w - 1));
                                builder.append((char)('A' + x - 1));
                                builder.append((char)('A' + y - 1));
                                builder.append((char)('A' + z - 1));
                                return builder.toString();
                            }
                            used[z] = false;
                        }
                        used[y] = false;
                    }
                    used[x] = false;
                }
                used[w] = false;
            }
            used[v] = false;
        }
        return "no solution";
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Solution4 test = new Solution4();
        List<String> resList = new ArrayList<>();
        int target = 0;
        while ((target = scanner.nextInt()) != 0) {
            String letters = scanner.next();
            resList.add(test.findPassword(letters, target));
        }
        for (String res : resList) {
            System.out.println(res);
        }
    }
}

 


5. 求解走马棋问题

【问题描述】有一个中国象棋中的“马”,在半张棋盘的左上角出发,右下角跳去。规定只允许向右跳(可上,可下,但不允许向左跳)求从起点A(1,1)到终点B(m,n)共有多少种不同的跳法。

【输入描述】输入多个测试用例,每个测试用例包括一行,各有两个正整数n、m,以输入n=0、m=0结束。
【输出描述】每个测试用例输出一行,表示相应的路径条数。

【输入样例】4 4
         0 0
【输出样例】2

public class Solution5 {

    private int chineseChess(int[] target) {
        traceBack(0, 0, target[0], target[1]);
        return res;
    }

    private int res = 0;

    private void traceBack(int posX, int posY, int targetX, int targetY) {
        if (posX == targetX && posY == targetY) {
            res++;
            return;
        }
        if (posX > targetX || posY > targetY) {
            return;
        }
        traceBack(posX + 1, posY + 2, targetX, targetY);
        traceBack(posX + 2, posY + 1, targetX, targetY);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Solution5 test = new Solution5();
        List<Integer> resList = new ArrayList<>();
        int[] target = new int[2];
        while ((target[0] = scanner.nextInt() - 1) != -1 && (target[1] = scanner.nextInt() - 1) != -1) {
            resList.add(test.chineseChess(target));
        }
        for (int res : resList) {
            System.out.println(res);
        }
    }
}

 


6. 求解最大团问题

【问题描述】一个无向图G中含顶点个数最多的完全子图称为最大团。输入含n个顶点(顶点编号为1~n)、m条边的无向图,求其最大团的顶点个数。
【输入描述】输入多个测试用例,每个测试用例第一行是n、m,接下来的m行,每行两个整数s、t,表示顶点s、t之间有一条边,以输入n=0、m=0结束。
【输出描述】每个测试用例输出一行,表示相应的最大团的顶点个数。

【输入样例】5 6
         1 2
         2 3
         2 4
         3 4
         3 5
         4 5
         0 0
【输出样例】3

public class Solution6 {

    private int maximumClique(boolean[][] graph) {
        traceBack(0, graph, new boolean[graph.length]);
        return maxCnt;
    }

    private int maxCnt;

    private void traceBack(int cnt, boolean[][] graph, boolean[] used) {
        maxCnt = Math.max(maxCnt, cnt);
        for (int i = 0; i < graph.length; i++) {
            if (!used[i] && check(i, graph, used)) {
                used[i] = true;
                traceBack(cnt + 1, graph, used);
                used[i] = false;
            }
        }
    }

    // 节点node能否放入当前子图中
    // 比较巧妙的一点是,所谓当前子图就是used为true的节点组成的子图
    private boolean check(int node, boolean[][] graph, boolean[] used) {
        for (int i = 0; i < graph.length; i++) {
            if (node == i) {
                continue;
            }
            // 如果i已经在当前子图中,且node没有与其相连,则node不能加入当前子图中
            if (used[i] && !graph[node][i]) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Solution6 test = new Solution6();
        int n, m;
        List<Integer> resList = new ArrayList<>();
        while ((n = scanner.nextInt()) != 0 && (m = scanner.nextInt()) != 0) {
            boolean[][] graph = new boolean[n][n];
            for (int i = 0; i < m; i++) {
                int node1 = scanner.nextInt() - 1;
                int node2 = scanner.nextInt() - 1;
                graph[node1][node2] = true;
                graph[node1][node2] = true;
            }
            resList.add(test.maximumClique(graph));
        }
        for (int res : resList) {
            System.out.println(res);
        }
    }
}

 


7. 求解幸运的袋子问题

【问题描述】一个袋子里面有n个球,每个球上面都有一个号码(拥有相同号码的球是无区别的)。如果一个袋子是幸运的当且仅当所有球的号码的和大于所有球的号码的积。
例如:如果袋子里面的球的号码是{1, 1, 2, 3},这个袋子就是幸运的,因为1 + 1 + 2 + 3 > 1 * 1 * 2 * 3 。
你可以适当从袋子里移除一些球(可以移除0个,但是别移除完),要使移除后的袋子是幸运的。现在让你编程计算一下你可以获得的多少种不同的幸运的袋子。

【输入描述】第一行输入一个正整数n(n ≤ 1000) ,第二行为n个数正整数xi(xi ≤ 1000) 。
【输出描述】输出可以产生的幸运的袋子数 。

【输入样例】3
         1 1 1
【输出样例】2

public class Solution7 {

    private int luckyBag(int[] nums) {
        Arrays.sort(nums);
        traceBack(0, 1, 0, nums);
        return cnt;
    }

    private int cnt;

    private void traceBack(int tempSum, int tempMul, int index, int[] nums) {
        if (tempSum > tempMul) {
            cnt++;
        }
        if (index == nums.length) {
            return;
        }
        for (int i = index; i < nums.length; i++) {
            // 极其巧妙的去重!!!
            if (i > index && nums[i] == nums[i - 1]) {
                continue;
            }
            traceBack(tempSum + nums[i], tempMul * nums[i], i + 1, nums);
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int len = scanner.nextInt();
        int[] nums = new int[len];
        for (int i = 0; i < len; i++) {
            nums[i] = scanner.nextInt();
        }
        System.out.println(new Solution7().luckyBag(nums));
    }
}

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

⭐️ 这一定是全网最优美的Java解法 >_<

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值