Algorithm 给定一个整数N和一组运算,以最少的步骤将N减为1
对于给定的整数N,我们可以使用以下操作:Algorithm 给定一个整数N和一组运算,以最少的步骤将N减为1,algorithm,Algorithm,对于给定的整数N,我们可以使用以下操作: 如果N可以除以3:除以3 如果N可以除以2:除以2 减去1 我如何找到一种策略,以最少的步骤达到1?将其视为宽度优先的树遍历 根节点是N。操作是指向其子节点的边 到达1时停止 从根到1的路径就是解决方案。将其视为宽度优先的树遍历 根节点是N。操作是指向其子节点的边 到达1时停止 从根到1的路径就是解决方案。有一种快速动态编程解决方案:- minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(
我如何找到一种策略,以最少的步骤达到1?将其视为宽度优先的树遍历 根节点是N。操作是指向其子节点的边 到达1时停止
从根到1的路径就是解决方案。将其视为宽度优先的树遍历 根节点是N。操作是指向其子节点的边 到达1时停止
从根到1的路径就是解决方案。有一种快速动态编程解决方案:-
minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
public static int decompose(int n) {
int steps [] = new int[n+1];
steps[1] = 0;
for(int i=2;i<=n;i++) {
int min = n;
if(i%2==0) {
min = Math.min(min,steps[i/2]);
}
if(i%3==0) {
min = Math.min(min,steps[i/3]);
}
min = Math.min(min,steps[i-1]);
steps[i] = min + 1;
}
int k =n;
System.out.println("Steps:");
while(k>1) {
if(k%3==0&&steps[k/3]+1==steps[k]) {
System.out.println("div 3");
k=k/3;
}
else if(n%2==0&&steps[k/2]+1==steps[k]) {
System.out.println("div 2");
k=k/2;
}
else {
System.out.println("minus 1");
k=k-1;
}
}
return(steps[n]);
}
注意:如果N不能被3或2整除,则不要将其包含在DP方程中
时间复杂性:O(N)
空间复杂性:O(N)
DP解决方案的Java代码:-
minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
public static int decompose(int n) {
int steps [] = new int[n+1];
steps[1] = 0;
for(int i=2;i<=n;i++) {
int min = n;
if(i%2==0) {
min = Math.min(min,steps[i/2]);
}
if(i%3==0) {
min = Math.min(min,steps[i/3]);
}
min = Math.min(min,steps[i-1]);
steps[i] = min + 1;
}
int k =n;
System.out.println("Steps:");
while(k>1) {
if(k%3==0&&steps[k/3]+1==steps[k]) {
System.out.println("div 3");
k=k/3;
}
else if(n%2==0&&steps[k/2]+1==steps[k]) {
System.out.println("div 2");
k=k/2;
}
else {
System.out.println("minus 1");
k=k-1;
}
}
return(steps[n]);
}
公共静态int分解(int n){
整数步[]=新整数[n+1];
步骤[1]=0;
对于(int i=2;i1){
如果(k%3==0&&steps[k/3]+1==steps[k]){
系统输出打印LN(“第3部分”);
k=k/3;
}
else如果(n%2==0&&steps[k/2]+1==steps[k]){
系统输出打印LN(“第2部分”);
k=k/2;
}
否则{
系统输出打印项次(“减1”);
k=k-1;
}
}
返回(步骤[n]);
}
有一种快速动态编程解决方案:-
minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
public static int decompose(int n) {
int steps [] = new int[n+1];
steps[1] = 0;
for(int i=2;i<=n;i++) {
int min = n;
if(i%2==0) {
min = Math.min(min,steps[i/2]);
}
if(i%3==0) {
min = Math.min(min,steps[i/3]);
}
min = Math.min(min,steps[i-1]);
steps[i] = min + 1;
}
int k =n;
System.out.println("Steps:");
while(k>1) {
if(k%3==0&&steps[k/3]+1==steps[k]) {
System.out.println("div 3");
k=k/3;
}
else if(n%2==0&&steps[k/2]+1==steps[k]) {
System.out.println("div 2");
k=k/2;
}
else {
System.out.println("minus 1");
k=k-1;
}
}
return(steps[n]);
}
注意:如果N不能被3或2整除,则不要将其包含在DP方程中
时间复杂性:O(N)
空间复杂性:O(N)
DP解决方案的Java代码:-
minSteps(N) = Minimum(minSteps(N/3),minSteps(N/2),minSteps(N-1)) + 1
public static int decompose(int n) {
int steps [] = new int[n+1];
steps[1] = 0;
for(int i=2;i<=n;i++) {
int min = n;
if(i%2==0) {
min = Math.min(min,steps[i/2]);
}
if(i%3==0) {
min = Math.min(min,steps[i/3]);
}
min = Math.min(min,steps[i-1]);
steps[i] = min + 1;
}
int k =n;
System.out.println("Steps:");
while(k>1) {
if(k%3==0&&steps[k/3]+1==steps[k]) {
System.out.println("div 3");
k=k/3;
}
else if(n%2==0&&steps[k/2]+1==steps[k]) {
System.out.println("div 2");
k=k/2;
}
else {
System.out.println("minus 1");
k=k-1;
}
}
return(steps[n]);
}
公共静态int分解(int n){
整数步[]=新整数[n+1];
步骤[1]=0;
对于(int i=2;i1){
如果(k%3==0&&steps[k/3]+1==steps[k]){
系统输出打印LN(“第3部分”);
k=k/3;
}
else如果(n%2==0&&steps[k/2]+1==steps[k]){
系统输出打印LN(“第2部分”);
k=k/2;
}
否则{
系统输出打印项次(“减1”);
k=k-1;
}
}
返回(步骤[n]);
}
正如姆贝基什所提到的,您可以将其视为BFS遍历,它比自底向上的DP方法具有更好的时间和空间复杂性。您还可以在遍历中应用分支和绑定(B&B)启发式排序,以便在我们之前已经看到标记值的节点上修剪树的分支。与实际的B&B启发式不同,这不会删减最佳解决方案,因为它不涉及对最佳解决方案可能位于何处的任何有根据的猜测。我将给出一个直观的示例,并将算法减少到0以更好地说明
下面是一个完整的操作树,将10减少到0:
--------10---------
5 -----9----
---4--- -3- ------8------
2 -3- 1 2 --4-- 7
1 1 2 0 1 2 -3- -----6------
0 0 1 0 1 1 2 2 -3- 5
0 0 0 1 1 1 2 --4--
0 0 0 1 2 -3-
0 1 1 2
0 0 1
0
因为我们在做BFS,我们实际上会在第一个零处停止,如下所示,而不是构建树的更深部分:
--------10------
5 -----9--------
---4--- -3- ------8------
2 -3- 1 2 4 7
1 1 2 0
然而,我们可以通过B&B启发式进一步减少分支的数量,使其看起来像这样(这对大量的分支产生了巨大的影响):
时间复杂度:O(logn)
空间复杂度:O(logn)
(我想)
下面是Python3代码,输入为1 googol(10^100),在我的计算机上运行大约需要8秒,内存大约为350MB。您也可以在以下位置在线运行它:
正如mbeckish提到的,您可以将其视为BFS遍历,它比自底向上的DP方法具有更好的时间和空间复杂性。您还可以在遍历中应用分支和绑定(B&B)启发式排序,以便在我们之前已经看到标记值的节点上修剪树的分支。与实际的B&B启发式不同,这不会删减最佳解决方案,因为它不涉及对最佳解决方案可能位于何处的任何有根据的猜测。我将给出一个直观的示例,并将算法减少到0以更好地说明 下面是一个完整的操作树,将10减少到0:
--------10---------
5 -----9----
---4--- -3- ------8------
2 -3- 1 2 --4-- 7
1 1 2 0 1 2 -3- -----6------
0 0 1 0 1 1 2 2 -3- 5
0 0 0 1 1 1 2 --4--
0 0 0 1 2 -3-
0 1 1 2
0 0 1
0
因为我们在做BFS,我们实际上会在第一个零处停止,如下所示,而不是构建树的更深部分:
--------10------
5 -----9--------
---4--- -3- ------8------
2 -3- 1 2 4 7
1 1 2 0
然而,我们可以通过B&B启发式进一步减少分支的数量,使其看起来像这样(这对大量的分支产生了巨大的影响):
时间复杂度:O(logn)
空间复杂度:O(logn)
(我想)
下面是Python3代码,输入为1 googol(10^100),在我的计算机上运行大约需要8秒,内存大约为350MB。您也可以在以下位置在线运行它:
你是说正确的答案是12,因为((12/3)/2)-1=1?是的。在10中,((10-1)/3)3)是正确的解,而不是((10/2)-1)/2))/2。@Nabla只要一个就足够了。在10中,答案是3。这让我想起了一点,其中
3n+1
是这里的两个步骤的倒数,/2
涵盖了第三个步骤。我不知道这是否意味着有比暴力更好的解决办法。也许值得在一个数学SE网站上讨论。@HyeonWooKim如果N不能被3,2整除怎么办?你是说正确的答案是12,因为((12/3)/2)-1=1?是的。在10中,((10-1)/3)3)是正确的解,而不是((10/2)-1)/2))/2。@Nabla只要一个就足够了。在10中,答案是3。这让我想起了一点,其中3n+1
是这里的两个步骤的倒数,/2
涵盖了第三个步骤。我不知道这是否意味着有比暴力更好的解决办法。也许值得在一个数学SE网站上发表一个话题。@HyeonWooKim如果N不能被3,2整除,那么d是什么呢