使用访问的int变量通过矩阵查找不同的组合?

时间:2018-06-22 15:30:50

标签: java

我正在这里查看这个topcoder问题:

http://community.topcoder.com/tc?module=ProblemDetail&rd=4725&pm=2288

在Java部分下有以下代码:

public class KiloManX {
    boolean ddd = false;

    int[] s2ia(String s) {
        int[] r = new int[s.length()];

        for (int i = 0; i < s.length(); i++) {
            r[i] = s.charAt(i) - '0' ;
        }
        return r;
    }

    public int leastShots(String[] damageChart, int[] bossHealth) {
        int i, j, k;
        int n = damageChart.length;
        int[][] dc = new int[n][];
        int[] cost = new int[1 << n];

        for (i = 0; i < n; i++) {
            dc[i] = s2ia(damageChart[i]) ;
        }
        for (i = 1; i < 1 << n; i++) {
            cost[i] = 65536 * 30000;

            for (j = 0; j < n; j++) {
                int pre = i - (1 << j);
                if ((i & (1 << j)) != 0) {
                    cost[i] = Math.min(cost[i], cost[pre] + bossHealth[j]) ;

                    for (k = 0; k < n; k++) {
                        if ((i & (1 << k)) != 0 && k != j && dc[k][j] > 0) {
                            cost[i] = Math.min(cost[i],
                                cost[pre] + (bossHealth[j] + dc[k][j] - 1) / dc[k][j]);
                        }
                    }
                }
            }
        }

        return cost[(1 << n) - 1] ;
    }

    static void pp(Object o) {
        System.out.println(o);
    }
}

我正试图了解他做了什么。所以我的理解是:

  1. i-以某种方式跟踪访问的节点(这是代码中最令人困惑的部分)

  2. j-是我们要击败的怪物

  3. k-是我们用来打败j的先前怪物的武器
  4. dc是字符串到矩阵的输入数组
  5. cost,保持每步成本,进行某种动态编程?我不知道cost[1 << n]如何给出结果?

我了解的是,他们正在经历所有可能的组合/组合。我感到困惑的是(即使在执行并出演了一个多星期之后):

  1. 他们如何跟踪所有组合?
  2. 我了解pre-击败前一个怪物的成本(我们在那里付出了多少成本),但我不知道您如何从{{ 1}}。

我已经执行了该程序(调试器),盯着它看了一个多星期,并试图对其进行解码,但是我对代码的位操作部分感到困惑。有人可以说明一下吗?

1 个答案:

答案 0 :(得分:1)

  

cost,保持每步成本,进行某种动态编程?

它们是部分成本,是的,但是将其表征为每步成本会错过该数组中索引的最重要意义。下面有更多内容。

  

我不知道cost[1 << n]如何给出结果?

当然,这本身不会带来任何结果。它只是声明一个带有2 n 个元素的数组。

  

他们如何跟踪所有组合?

请参阅下文。这与为何cost数组声明其大小密切相关。

  

我了解pre-击败前一个怪物的成本(即我们在那里花费的成本),但我不知道您是如何从(i - 1 << j)获得的。

当然pre本身并不是成本。但是,它有条件地用作cost数组的 index 。现在考虑条件:

                if ((i & (1 << j)) != 0) {

表达式i & (1 << j)测试j值的位i是否设置。如果是i - (1 << j) pre),则求值为j值的第i位关闭的结果。这应该为您提供线索,因为cost的索引是位掩码。该数组(1 << n)的大小是另一个线索:它是不同的n位比特掩码的数量。

这里的技巧是一个相对普遍的技巧,也是一个很好的认识的技巧。假设您有一组N个对象,并且希望以某种方式表示其所有子集(==其元素的所有不同组合)。每个子集的特征在于N个对象中的每一个是否是一个元素。您可以将其表示为N个数字,每个数字为0或1- N位。现在假设您将这些位串在一起成为N位数字。从0(含)到2 N (不含)的每个整数都有其最低有效N位的不同模式,因此每个对应于不同的子集。

呈现的代码正是使用这种对应关系来将老板组的不同子集编码为cost数组中的不同索引,从而回答了您另一个如何跟踪组合的问题。给定一个这样的索引i表示包含老板j的子集,索引i - (1 << j)表示通过删除老板j从中获得的集合。

然后,粗略地讲,该程序通过检查从少一个元素的子集形成它的所有方法来优化每个非空子集的成本,从而继续进行程序。 (1 << n) - 1是对应于整个集合的索引,因此,cost的元素最后包含整体优化值。