在TopMode的SRM 655 Div 2 Hard中使用掩码

时间:2015-04-17 11:33:01

标签: algorithm dynamic-programming bitmask

问题如下......

鲍勃的小妹妹爱丽丝已经九岁了。鲍勃正在测试她的数学实力,要求她计算一个数除以9时给出的余数。

今天,鲍勃向爱丽丝提出了N个问题。我们将问题编号为0到N-1。在每个问题中,Bob都给了Alice相同的M位数字。 (请注意,Bob的号码允许有一些前导零。)

在某些情况下,Alice在阅读该号码时可能会跳过部分数字。然而,她在计算中从未犯过任何其他错误。例如,如果Bob给爱丽丝的号码为012345三次,她可能第一次将其读为0145,第二次读为012345,第三次为135。然后,她的答案是145模9 = 1,12345模9 = 6,135模9 = 0。

您将获得带有M个元素的int N和一个int [] d。对于每个i,数字d [i]对应于Bob的数字中的数字10 ^ i的数字。对于每个i和j,Alice在回答问题j时读取数字i,当且仅当数字d [i]的位数j为1时。

例如,假设d [3] = 6.在二进制中,6是110.换句话说,二进制数字0,1和2是0,1和1.因此,Alice跳过相应的问题0中的数字,但她在问题1和2中阅读。

在今天的实验中发生了一件令人惊讶的事情:对于N个问题中的每个问题,Alice的回答是余数是0. Bob发现这很有趣。他现在想知道:给定N和d,有多少个不同的M位数字有这个属性?

让X成为鲍勃问题的答案。计算并返回值(X modulo 1,000,000,007)。

在考虑了解决方案已经有一段时间了,并且不知道出现之后,我参考了下面的社论

添加,不要乘以

当问题要求你专门使用模9做事时(除以9除外),值得知道一个关于模9和数字的特殊属性。 10≡1mod9,这意味着10的所有幂也是1模9。模数运算将使得如果你取一个数字,快速获得模数9的数字是首先添加其数字然后取余数除以9.例如:4671mod9≡(4⋅1000+6⋅100+7⋅10+ 1)mod9≡4⋅1+6⋅1+7⋅1+1⋅1mod9≡4+ 6 + 7 + 1。

N个模数

让我们考虑从0到M-1的顺序决定每个数字。有N个问题,每个问题代表模数9的一些数字的总和。我们需要每个问题的最终总和为0,但在填写每个数字时,当前总和可能会有所不同并且是不同的到0。

让我们把这看作是一个国家问题。我们从空的M位数字开始,我们希望填写它。最初,N个问题中每个问题的所有数字总和都是0.想象一下,我们决定索引为0的数字将为7.这意味着一些总和s将变为(s + 7)mod9。我们不需要记住整个总和,只需要它的模9。其他一些金额将保持为0.这取决于具体问题的总和中是否包含0。

这允许我们考虑像f(s0,s1,...,sN-1,p)这样的重现。这将为我们提供填充索引大于或等于p的数字的方式的数量,这样si是每个问题的当前总和。

基本情况:p = M,这意味着我们已经用完了数字。我们不再需要添加任何数字,并且总和不能再改变,所有si必须等于0。 否则,我们可以尝试将每个数字i作为将在数字的位置p中使用的数字,并查看状态如何变化。新的总和将是s' 0,s' 1,... s' N-1并且它们将根据数字#p是否包括在相应的总和中而添加i。填充剩余位置的方式的数量是:f(s' 0,s' 1,... s' N-1,p + 1)。我们需要为我们尝试的每个数字添加此结果。 这种复发解决了这个问题。它是非循环的,州的规模不是很大。注意,每个si可以是9个数字中的一个(结果模9),因此最多有95种不同的si组合。 p是O(M),M≤20。对于每个州,我们需要尝试9位数并更新5个总和。如果我们使用memoization或迭代地实现函数,最坏的情况将看起来像95⋅20⋅9⋅5,这非常适合时间限制。

由于参数的灵活性,实现memoization可能会很复杂。我们可以通过两种方式解决这个问题。一个假设总有5个问题,但是5-M的问题忽略了所有数字。

  

另一种是使用base-9编码来表示状态。

     

这与使用位掩码(基数2)非常类似,但使用基数   9。

我理解几乎所有内容,包括动态编程状态的选择。但我无法理解的是在解决方案中使用了面具。有人可以解释它是如何完成的,即掩码的实现。

以下是编辑中提供的代码

const int MOD = 1000000007;

int pow9[6];

int N;
vector<int> d;

long dp[9*9*9*9*9][21];

long f(int mask, int p)
{
    long & res = dp[mask][p];
    if (res == -1) {
        res = 0;
        if (p == d.size() ) {
            // base case
            if (mask == 0) {
                // good
                res = 1;
            }
        } else {
            // pick a digit for the number
            for (int i = 0; i <= 9; i++) {
                // calculate the new mask:
                int mask2 = 0;
                for (int j = N-1; j >= 0; j--) {
                    int o = (mask / pow9[j]) % 9;
                    if ( (d[p] & (1<<j)) != 0 ) {
                        o = (o + i) % 9;
                    }
                    mask2 = mask2 * 9 + o;
                }
                res += f(mask2, p+1);
            }
            res %= MOD;
        }
    }
    return res;
}

int count(int N, vector<int> d)
{
    this->d = d;
    this->N = N;
    memset(dp, -1, sizeof(dp));
    pow9[0] = 1;
    for (int i = 1; i <= N; i++) {
        pow9[i] = 9 * pow9[i-1];
    }
    return (int)f(0,0);
}

注意:我稍微了解了一下掩码,我在子集求和问题中使用了它们。虽然它与这个实现完全不同。

任何帮助将不胜感激。提前谢谢!

0 个答案:

没有答案