给定数字N(< = 10000),找到总和为N的最小primatic数字。
一个primatic数字指的是一个数字,它可以是一个素数,也可以表示为素数对它自身的幂,即素数。 4,27等
我尝试使用seive找到所有的primatic数字,然后将它们存储在一个向量中(下面的代码),但现在我无法查看如何找到总和为给定数字的最小primatic数字。< / p>
这是我的筛子:
#include<algorithm>
#include<vector>
#define MAX 10000
typedef long long int ll;
ll modpow(ll a, ll n, ll temp) {
ll res=1, y=a;
while (n>0) {
if (n&1)
res=(res*y)%temp;
y=(y*y)%temp;
n/=2;
}
return res%temp;
}
int isprimeat[MAX+20];
std::vector<int> primeat;
//Finding all prime numbers till 10000
void seive()
{
ll i,j;
isprimeat[0]=1;
isprimeat[1]=1;
for (i=2; i<=MAX; i++) {
if (isprimeat[i]==0) {
for (j=i*i; j<=MAX; j+=i) {
isprimeat[j]=1;
}
}
}
for (i=2; i<=MAX; i++) {
if (isprimeat[i]==0) {
primeat.push_back(i);
}
}
isprimeat[4]=isprimeat[27]=isprimeat[3125]=0;
primeat.push_back(4);
primeat.push_back(27);
primeat.push_back(3125);
}
int main()
{
seive();
std::sort(primeat.begin(), primeat.end());
return 0;
}
答案 0 :(得分:0)
一种方法可以是在排序列表中存储小于或等于N的所有primatics - 调用此列表L
- 并递归搜索最短序列。最简单的方法是“贪婪”:尽早选择最大的跨度/数字。
N = 14
您有L = {2,3,4,5,7,8,9,11,13}
,因此您需要制作尝试这些序列的算法/流程:
13
太小13 + 13
- &gt; 13 + 2
会太大11
太小11 + 11
- &gt; 11 + 4
会太大11 + 3
是匹配。您可以通过在每次需要另一个primatic时使搜索功能递归来继续此过程,您的目标是发生最少次数。为此,您可以选择最大的 - &gt;每个位置中最小的primatic(总和中的第1个,第2个等),并且只有当到目前为止总和中的primatics足够小以至于另外的primatic不会超过N
时,才包含另一个数字。
我必须做一个工作示例来找到一个足够小的N
,它不会导致总和中只有2个数字。请注意,因为你可以将任何自然数表示为最多4个自然数的平方和,并且你有一个比正方形组更密集的L
集,所以我认为你很少有对于您想要手动计算的任何N
,结果为3或更多。
我必须澄清“贪婪”与“动态编程”不同,它可以给出次优结果。这确实有一个DP解决方案。同样,我不会在代码中编写最终过程,而是将其作为从中制作工作DP解决方案的参考点。
要做到这一点,我们需要从下到上建立解决方案。您需要的是一种结构,可以为所有数字N
存储已知解决方案,此列表可以以最佳方式逐步添加到更大的N
。
考虑对于任何N
,如果它是primatic,那么N
的术语数量只是1.这适用于N=2-5,7-9,11,13,16,17,19
。所有其他N
的术语数必须至少为2,这意味着它是两个primatics的总和,或者是primatic和其他N
的总和。 / p>
前几个不小的例子:
6 - 可以是2+4
或3+3
,此处的所有条款本身都是primatic,因此6的最小条款数为2。
10 - 可以是2+8
,3+7
,4+6
或5+5
。然而,6不是primatic,并采取该解决方案至少留下2个术语。
12 - 可以是2+10
,3+9
,4+8
,5+7
或6+6
。其中6+6
和2+10
包含非primatics,而其他则不包含,因此2个术语最少。
14 - 同上,存在两种解决方案:3+11
,5+9
,7+7
。
用于存储所有这些解决方案的结构需要能够跨越相同等级/数量的术语的解决方案。您已经有一个primatics列表,这也是只需要一个术语的解决方案列表。
Sol[term_length] = list(numbers)
。您还需要一个函数/缓存来查找一些N
的最短项长度,例如S(N) = term_length iif N in Sol[term_length]
Sol[1] = {2,3,4,5 ...}
及以后, Sol[2] = {6,10,12,14 ...}
和Sol[3]
等等。
可以使用来自Sol[1]
的一个术语来找到任何解决方案。任何需要两个primatics的解决方案都可以在Sol[2]
中找到。任何需要3的解决方案都在Sol[3]
等。
您需要认识到的是,对于某些S(N) = 3
primatics,Sol[1][a] + Sol[1][b] + Sol[1][c]
可以表示为a,b,c
,但它也可以表示为Sol[1][a] + Sol[2][d]
,因为所有Sol[2]
必须表达为Sol[1][x] + Sol[1][y]
。
此算法实际上会针对给定的Sol[1]
搜索N
,然后查看Sol[1] + Sol[K]
并增加K
,但要执行此操作,您需要S
}和Sol
结构大致采用此处显示的形式(或者能够以类似方式访问/查询)。
使用以上作为指导我快速将它们放在一起,甚至可以显示它使用的多项和。
如果你愿意,我可以深入解释代码,但真正的DP部分是在40-64行左右。递归深度(也是总和中附加项的数量)是k
,一个简单的双迭代器,而循环检查是否可以使用第k个已知解和primatics求和,如果是,那么我们就完成了如果没有,那么检查k + 1解决方案,如果有的话。 Sol
和S
按照描述工作。
唯一令人困惑的部分可能是使用反向迭代器,它只是使!= end()
检查对于while条件是一致的(end不是有效的迭代器位置但是start是,所以!= begin
将是用不同的方式写。)
编辑 - 仅供参考,第一个至少需要3个术语的数字是959
- 必须运行我的算法到1000个数字才能找到它。它总结为6 + 953(primatic),无论你如何分裂6,它仍然是3个术语。