【问题标题】:Dividing an even number in the most efficient way以最有效的方式除偶数
【发布时间】:2016-11-29 12:04:10
【问题描述】:

我需要一个用 c++ 编写的程序来获取一个数字:

循环:

如果该数字是偶数除以 2 (n=n/2) 如果不是,您可以执行以下操作之一: n+1 n-1

循环结束


程序应该这样做直到 n=1。 但它应该以最有效和最快的方式做到这一点,我唯一的提示是我可以使用 DP 方法。 输出应该是用于计算该数字的操作数。 例如:

15->16->8->4->2->1 输出:5

35->36->18->9->8->4->2->1 输出:7

这是我写的代码,但它还没有完成,这是错误的,因为我无法弄清楚我应该如何在每个步骤中添加或减去:

#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
    int n;
    int h=0;
    int i=0;
    cout<<"Enter A Number:";
    cin >> n;
    int r=n;
    int q=n;
    cout<<r;
    L1: while ( r % 2 == 0)
    {
        for(int m=0;r>1 && m==0 ;)
        {   r=r / 2;h++;
        m=r%2;
    cout<<" => "<<r;
        }}
    while(r%2==1 && r>1)
        {r=r-1;cout<<" => "<<r;h++;
    goto L1;}
    cout<<endl;

    //**********************


    cout<<q;
    L2: while ( q % 2 == 0)
    {
        for(int m=0;q>1 && m==0 ;)
        {   q=q / 2;i++;
        m=q%2;
    cout<<" => "<<q;
        }}
    while(q%2==1 && q>1)
        {q=q+1;cout<<" => "<<q;i++;
    goto L2;}
    cout<<endl<<"First:"<<h<<endl<<"Second:"<<i<<endl;
system("pause");


}

【问题讨论】:

  • 到目前为止你尝试过什么?
  • 有趣的问题。但是你为什么不想自己去尝试一下呢?
  • 我会尽量避免显式应用这些操作,而是寻找一种更直接的方法来计算f(n),这样f 就是所需的操作数。我的第一个猜测是尝试找出从 n 到 2 的幂需要多少步(因为从那时起,获取步数就变得微不足道了)
  • 任何代码都比没有代码更尴尬:P
  • @Prometheus 对不起,你们是对的,我添加了代码

标签: c++ dynamic-programming


【解决方案1】:

如果您想使用 DP 解决它

我会这样说:对于每个可能的值 1

考虑起始状态是 (15,0): 15 零步。 由此您可以创建两个新状态 (8,2) 和 (7,2),因为每个状态都需要两个步骤(加/减 + 除)。

提取 (8,2): (7,2)(4,3)

提取 (7,2): (4,3)(3,4) DP来了! (7,2) 将创建状态 (4,4),但您在队列中只保留相同状态的最小步数。

提取 (4,3): (2,4)(3,4)

提取(3,4): (2,4)(1,6)

提取 (2,4): (1,5)

这就是解决方案是 5 个步骤。

35 步:

(35,0) --- >(18,2) (17,2) -----> (17,2) (9,3) ----->

(9,3)(8,4) ----> (8,4)(5,5)(4,5) ----> (5,5)(4,5) -- --->

(4,5)(3,7)(2,7)----> (3,7)(2,6) -----> (2,6)(1,9) - ---> (1,7)

解决方案:7 个步骤。

【讨论】:

    【解决方案2】:

    看看对你有没有帮助。

    // Example program
    #include <iostream>
    #include <string>
    
    int f (int n)
    {
        int iterations = 0;
    
        while (n > 1)
        {
            if (n % 2 != 0)
            {
                std::cout << n << "->";
                ++n;
                if (n & (n - 1))
                    n -= 2;
    
                ++iterations;
            }
            std::cout << n << "->";
            n >>= 1;
            ++iterations;
        }
    
        std::cout << n << "->";
        return iterations;
    }
    
    int main()
    {
        std::cout << f(15) << std::endl;
        std::cout << f(41) << std::endl;
        std::cout << f(43) << std::endl;
    }
    

    【讨论】:

    • 算法看起来相当不错。
    【解决方案3】:

    对于动态规划的使用,您应该进行递归以获得问题的子解决方案,然后解决问题本身。您还必须使用内存结构来保存此类子解决方案的结果。

    #include <deque>
    #include <iostream>
    
    using namespace std;
    
    int solve(deque<int>& solution, int number) {
        if(number >= solution.size())           // resize to fit
            solution.resize(number + 1, -1);
        if(number == 1)                         // special case for number 1
            return solution[number] = 0;
        if(solution[number] != -1)              // if already calculated
            return solution[number];
        if(number % 2 == 0)                     // n=n/2
            return solution[number] = solve(solution, number/2) + 1;
        int solutionA = solve(solution, number + 1);    // n++
        int solutionB = solve(solution, number - 1);    // n--
        return solution[number] = std::min(solutionA, solutionB) + 1; // best of n++,n--
    }
    
    int main() {
        deque<int> solution;
        cout << solve(solution, 35);
    }
    

    我不确定代码是否可以正常工作。

    【讨论】:

    • 我认为如果使用动态编程,解决方案必须比这更聪明。这可能会很快耗尽任何大值的内存。不过,这是一种测试算法正确性的好方法。
    • 好吧,你可以用std::map替换std::deque,它会以牺牲速度为代价来节省内存,而且会增加一些代码膨胀。
    【解决方案4】:

    这是我的递归解决方案,已针对 DP 示例验证高达 2097152。 它的基础是使用最后两位的值来确定最佳操作。如果最后一位是0,我们总是除法。如果最后两位是11,我们总是递增,因为它转换为100,这使得两个连续的除法操作成为可能。 如果最后两位是01,我们会递减,因为这给了我们的下一个操作两个连续的除法操作,而递增给我们10

    极端情况是数字 3,其中 3 -> 2 比提升为 4 更理想。

    我怀疑您可以通过扫描位模式来进一步优化此操作以确定所需的操作数。即每个零都需要一个 div 操作,一组 1 可以通过一次加法变为零。

    #include <cstdint>
    
    int solve_algorithmically(std::uint64_t number)
    {
        // If 1 there is nothing to do.
        if (number <= 1)
            return 0;
    
        // Nasty hack to get around the case where number=3 & 3 == 3 will cause increment 
        if (number == 3)
            return solve_algorithmically(number - 1) + 1;
    
        // If we have an even number (0 in LSB)
        if ((number & 1) == 0)
            return solve_algorithmically(number / 2) + 1;
    
        // If we have two consecutive 1's i.e. (...11) then increment as this wil give us two zeroes.
        // The exception is the root case 3 where decrement wins.
        if ((number & 3) == 3)
            return solve_algorithmically(number + 1) + 1;
    
        // The only other case ends last two bits = 01
        return solve_algorithmically(number - 1) + 1;
    }
    
    int main() {
        for (auto i = 1; i < 2097152; i++)
        {
            int alg = solve_algorithmically(i);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-24
      • 1970-01-01
      相关资源
      最近更新 更多