鉴于:设置A = {a0, a1, ..., aN-1}
(1 ≤ N ≤ 100
),2 ≤ ai ≤ 500
。
问:查找大小至少为A
的所有子集的所有最小公倍数(LCM)的总和。
集合B = {b0, b1, ..., bk-1}
的最小公倍数被定义为Bmin
的最小整数bi | Bmin
,对于所有0 ≤ i < k
。
示例:
让N = 3
和A = {2, 6, 7}
,然后:
LCM({2, 6}) = 6
LCM({2, 7}) = 14
LCM({6, 7}) = 42
LCM({2, 6, 7}) = 42
----------------------- +
answer 104
天真的方法是简单地计算所有O(2N)
子集的最小公倍数,这对于相当大的N
来说是不可行的。
解决方案草图:
问题来自竞赛 * ,它也提供了solution sketch。这就是我的问题所在:我不理解暗示的方法。
解决方案读取(模数一些小的固定语法问题):
解决方案有点棘手。如果我们仔细观察,我们会发现整数介于
2
和500
之间。因此,如果我们对数字进行素数分解,我们将获得以下最大权力:
2 8
3 5
5 3
7 3
11 2
13 2
17 2
19 2
除此之外,所有质数都有幂1.因此,我们可以使用这些整数轻松计算所有可能的状态,留下
9 * 6 * 4 * 4 * 3 * 3 * 3 * 3
个状态,几乎为70000
。对于其他整数,我们可以制作如下所示的dp:dp[70000][i]
,其中i
可以是0
到100
。但是,由于dp[i]
取决于dp[i-1]
,因此dp[70000][2]
就足够了。这使得n * 70000
的复杂性变得可行。
我有以下具体问题:
dp
是否代表动态编程?若然,是什么重现关系正在解决?dp[i]
如何计算dp[i-1]
?0
或1
次。对于每个素数,状态数是否应该乘以2
(再次导致不可行的状态空间)?* 原始问题描述可以从this source找到(问题F)。这个问题是该描述的简化版本。
答案 0 :(得分:3)
社论的第一部分似乎很有用,但第二部分相当含糊(也许没有用;我宁愿完成这个答案而不是弄清楚)。
让我们假设输入由成对不同的素数组成,例如2,3,5和7.然后答案(用于求和所有集合,其中0整数的LCM为1)是
(1 + 2) (1 + 3) (1 + 5) (1 + 7),
因为子集的LCM与此处的乘积完全相同,所以只需将其相乘。
让我们放松素数成对不同的限制。如果我们有一个像2,2,3,3,3和5这样的输入,那么乘法就像
(1 + (2^2 - 1) 2) (1 + (2^3 - 1) 3) (1 + (2^1 - 1) 5),
因为2出现多重性2,而3出现多重性3,而5出现多重性1.对于,例如,只有3的集合,有2^3 - 1
种方式来选择包含一个3和1
方式来选择空集。
如果它是19或更少,则调用 small ,否则调用 large 。注意,整数500或更少可被最多一个大素数(具有多重性)整除。小素数更有问题。我们要做的是计算LCM的素数因子分解的每个可能的小部分(即~70,000个状态之一),通过丢弃不能除的整数得到的问题的LCM之和这样的LCM并且只留下其他整数的大素数因子(或1)。
例如,如果输入是2,30,41,46和51,状态是2,那么我们将2保留为1,丢弃30(= 2 * 3 * 5; 3和5很小)保留41为41(41为大),保留46为23(= 2 * 23; 23为大),丢弃51(= 3 * 17; 3和17为小)。现在,我们使用前面描述的技术计算LCM的总和。使用inclusion-exclusion去除其小部分正确划分状态而不是完全相等的LCM的子集。也许我稍后会做一个完整的例子。
答案 1 :(得分:3)
在阅读了实际的比赛描述(page 10 or 11)和解决方案草图之后,我必须得出结论,解决方案草图的作者在写作时非常不精确。
高级问题是如果通过公平硬币投掷随机选择组件,则计算预期寿命。这就是计算所有子集的最小公倍数的原因 - 所有子集都有效地代表了样本空间。您最终可能会得到任何可能的组件。设备的故障时间基于集合的LCM。因此,预期寿命是所有集合的LCM的平均值。
请注意,这应该包括只有一个项目的集合的LCM(在这种情况下,我们假设LCM是元素本身)。解决方案草图似乎破坏了,也许是因为他们以不太优雅的方式处理它。
草图作者仅使用单词 state 两次,但显然设法切换含义。在第一次使用 state 这个词时,他们似乎在谈论可能选择的组件。在第二次使用中,他们可能会谈论可能的失败时间。他们可能会混淆这个术语,因为他们的动态编程解决方案通过对单词的一次使用来初始化值,并且递归关系源于另一种。
我会说它确实或者它是巧合,因为解决方案草图似乎非常暗示动态编程。
我能想到的是,在他们的解决方案中, state i
表示失败的时间T(i)
,此次失败的次数已被计算在内,dp[i]
。得到的总和将是所有dp[i] * T(i)
的总和。
dp[i][0]
将仅计算第一个组件的失败时间。然后dp[i][1]
将计算第一个和第二个组件的失败时间。 dp[i][2]
将用于第一,第二和第三。等。
使用零初始化dp[i][0]
,但dp[T(c)][0]
(其中c
是第一个被考虑的组件)除外,该值应为1(因为此组件的失败时间已被计算一次,因此远)。
从dp[i][n]
为每个组件dp[i][n-1]
填充c
:
i
,请将dp[i][n-1]
复制到dp[i][n]
。dp[T(c)][n]
。i
,请将dp[i][n-1]
添加到dp[LCM(T(i), T(c))][n]
。这是做什么的?假设您知道自己有时间失败j
,但是您添加了k
失败时间的组件。无论您以前使用过什么组件,新的失败时间都是LCM(j, k)
。这是因为对于两组A
和B
,LCM(A union B} = LCM(LCM(A), LCM(B))
。
同样,如果我们考虑T(i)
失败的时间以及我们的新组件T(c)
失败的时间,则导致失败的时间为{{1} }。请注意,我们将此时间记录为LCM(T(i), T(c))
配置失败,因此我们应该在引入新组件后记录许多新的失败时间。
每个都发生0或1次。每个素数的状态数是否应该乘以2(再次导致不可行的状态空间)?
当然,你是对的。但是,解决方案草图指出具有大质数的数字以另一种(未指定的)方式处理。
如果我们确实包含它们会发生什么?我们需要表示的州的数量会爆炸成一个不切实际的数字。因此,作者对这些数字的解释不同。注意,如果小于或等于500的数字包括大于19的素数,则其他因子乘以21或更小。这使得这些数字适合强暴,不需要任何表格。
答案 2 :(得分:0)
What is meant by these states?
我想在这里,状态指的是数字是否在集合B的{B0,b1,...,bk-1}的集合中。
Does dp stand for dynamic programming and if so, what recurrence relation is being solved?
How is dp[i] computed from dp[i-1]?
我们可以从以前的状态中找出下一组LCM的状态是可行的。所以,我们只需要2的数组,并来回切换。
Why do the big primes not contribute to the number of states? Each of them occurs either 0 or 1 times. Should the number of states not be multiplied by 2 for each of these primes (leading to a non-feasible state space again)?
我们只能使用Prime Factorization和exponents来表示数字。
这是一个例子。
6 =(2 ^ 1)(3 ^ 1)(5 ^ 0) - >州&#34; 1 1 0&#34;代表6 18 =(2 ^ 1)(3 ^ 2)(5 ^ 0) - >州&#34; 1 2 0&#34;代表18
以下是我们如何使用Prime Factorization
获得6和18的LMCLCM(6,18)=(2 ^(max(1,1))(3 ^(max(1,2))(5 ^ max(0,0))=(2 ^ 1)(3 ^ 2)(5 ^ 0)= 18
2 ^ 9&gt; 500,3 ^ 6> 500,5 ^ 4> 500,7 ^ 4> 500,11 ^ 3> 500,13 ^ 3> 500,17 ^ 3> 500,19 ^ 3> 500
我们只能使用素数2,3,5,7,11,13,17,19的指数计数来表示集B中的LCM = {b0,b1,...,bk-1} 对于给定的集合A = {a0,a1,...,aN-1}(1≤N≤100),其中2≤ai≤500。
9 * 6 * 4 * 4 * 3 * 3 * 3 * 3&lt; = 70000,所以我们只需要两个dp [9] [6] [4] [4] [3] [3] [3] ] [3]以跟踪所有LCM&#39;状态。所以,dp [70000] [2]就足够了。
我把一个小的C ++程序放在一起来说明我们如何得到给定集合A = {a0,a1,...,aN-1}(1≤N≤100)的LCM之和,其中2≤ai ≤500。在解决方案草图中,我们需要循环通过70000最大可能的LCM。
int gcd(int a, int b) {
int remainder = 0;
do {
remainder = a % b;
a = b;
b = remainder;
} while (b != 0);
return a;
}
int lcm(int a, int b) {
if (a == 0 || b == 0) {
return 0;
}
return (a * b) / gcd(a, b);
}
int sum_of_lcm(int A[], int N) {
// get the max LCM from the array
int max = A[0];
for (int i = 1; i < N; i++) {
max = lcm(max, A[i]);
}
max++;
//
int dp[max][2];
memset(dp, 0, sizeof(dp));
int pri = 0;
int cur = 1;
// loop through n x 70000
for (int i = 0; i < N; i++) {
for (int v = 1; v < max; v++) {
int x = A[i];
if (dp[v][pri] > 0) {
x = lcm(A[i], v);
dp[v][cur] = (dp[v][cur] == 0) ? dp[v][pri] : dp[v][cur];
if ( x % A[i] != 0 ) {
dp[x][cur] += dp[v][pri] + dp[A[i]][pri];
} else {
dp[x][cur] += ( x==v ) ? ( dp[v][pri] + dp[v][pri] ) : ( dp[v][pri] ) ;
}
}
}
dp[A[i]][cur]++;
pri = cur;
cur = (pri + 1) % 2;
}
for (int i = 0; i < N; i++) {
dp[A[i]][pri] -= 1;
}
long total = 0;
for (int j = 0; j < max; j++) {
if (dp[j][pri] > 0) {
total += dp[j][pri] * j;
}
}
cout << "total:" << total << endl;
return total;
}
int test() {
int a[] = {2, 6, 7 };
int n = sizeof(a)/sizeof(a[0]);
int total = sum_of_lcm(a, n);
return 0;
}
Output
total:104
答案 3 :(得分:-2)
各州不仅仅是素数的力量。你的数字最多为2 ^ 8,所以2的幂是[0..8],这是9个状态。与其他州类似。
“dp”很适合动态编程,我不确定。
复发关系是问题的核心,因此您将通过自己解决问题来学习更多知识。从一些简单的小例子开始。
对于大素数,尝试在不使用它们(或它们的等价物)的情况下解决减少的问题,然后将它们重新添加以查看它们对最终结果的影响。