数字形成

时间:2017-07-28 17:01:10

标签: algorithm numbers dynamic-programming

给定三个整数x,y和z,你需要找到最多4次形成的所有数字之和,最多5次y次,最多6次z次数。

注意:这些数字只能包含4,5,6个数字。

EG: 1 1 1

输出: 3675 ​​

说明: 输入的ans是 4 + 5 + 6 + 45 + 54 + 56 + 65 + 46 + 64 + 456 + 465 + 546 + 564 + 645 + 654 = 3675 ​​

我尝试使用类似于查找丑陋数字的DP方法。但没希望?

如何解决这个问题?

我认为这是一个超级难题。是吗?

2 个答案:

答案 0 :(得分:3)

这个问题有一个简单的两部分解决方案。

你需要:

  1. 用于查找数组的所有不同排序的算法。
  2. 一种创建数组的算法,其中感兴趣的数字包含不同的次数。
  3. 对于(1),您可以使用std::next_permutation()unordered_set

    对于(2),你可以构建一个构造数组的递归函数。

    以下程序实现了这一目标:

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <numeric>
    
    //Convert an array of digits into an integer
    int VecToNumber(const std::vector<int> &to_permute){
      int num   = 0;
      int tens  = 1;
      for(int i = to_permute.size()-1;i>=0;i--,tens*=10)
        num+=to_permute[i]*tens;
      return num;
    }
    
    void Permuter(std::vector<int> to_permute, std::vector<int> &numbers_to_add){
      //Sorting is a necessary step before we can use `std::next_permutation`
      std::sort(to_permute.begin(),to_permute.end());
      //Loop through every permutation of `to_permute`
      do {
        numbers_to_add.push_back(VecToNumber(to_permute));
      } while(std::next_permutation(to_permute.begin(), to_permute.end()));
    }
    
    //Build an array to permute
    void Builder(
      const std::vector<int> &values,   //Digits to use
      const std::vector<int> &counts,   //Maximum times to use each digit
      std::vector<int> &to_permute,     //Current array
      std::vector<int> &numbers_to_add, //Numbers we will be adding
      int pos                           //Digit we are currently considering
    ){
      //Since to_permute is used at each level of recursion, we must preserve it
      //at each level so we can reverse the effects of deeper levels of
      //recursion when moving back to shallower levels.
      const auto original_tp = to_permute;
    
      if(pos<values.size()){
        //Add more and more copies of a digit to the `to_permute` array, up to
        //the value specified by `counts[pos]`
        for(int i=0;i<counts[pos];i++){
          Builder(values,counts,to_permute,numbers_to_add,pos+1);
          to_permute.push_back(values[pos]);
        }
        Builder(values,counts,to_permute,numbers_to_add,pos+1);
      } else {
        //We've run out of digits to consider, now we will generate all of the
        //permutations of those digits
        Permuter(to_permute,numbers_to_add);
      }
      to_permute = original_tp;
    }
    
    int main(){
      std::vector<int> values = {{4,5,6}}; //Digits to use
      std::vector<int> counts = {{1,1,1}}; //Maximum number of times to use each digit
    
      std::vector<int> to_permute;     //Holds numbers we are currently permuting
      std::vector<int> numbers_to_add; //Holds numbers that we wish to add together
    
      //Collect all numbers we want to add together
      Builder(values,counts,to_permute,numbers_to_add,0);
    
      for(auto x: numbers_to_add)
        std::cout<<x<<std::endl;
    
      std::cout<<"Sum = "<<std::accumulate(numbers_to_add.begin(),numbers_to_add.end(),0)<<std::endl;
    }
    

    输出:

    0
    4
    5
    6
    45
    46
    54
    56
    64
    65
    456
    465
    546
    564
    645
    654
    Sum = 3675
    

答案 1 :(得分:1)

我最近发布了an answer to a (somewhat) related question。使用这种方法的想法是,对于每种可能的大小组合,您可以找到具有这些大小的数字位置的所有可能分区:

from itertools import product

def partitions(*sizes):
    if not sizes or all(s <= 0 for s in sizes):
        yield ()
    for i_size, size in enumerate(sizes):
        if size <= 0:
            continue
        next_sizes = sizes[:i_size] + (sizes[i_size] - 1,) + sizes[i_size + 1:]
        for p in partitions(*next_sizes):
            yield (i_size,) + p

def sum_numbers(*numbers):
    values, sizes = zip(*numbers)
    total = 0
    for p in product(*map(lambda s: range(s + 1), sizes)):
        for q in partitions(*p):
            total += sum(values[idx] * (10 ** pos) for pos, idx in enumerate(q))
    return total

示例:

sum_numbers((4, 1), (5, 1), (6, 1))
>>> 3675

说明:

partitions是一个返回类似&#34;排列而不重复&#34;的函数。例如,partitions(2, 3, 1)将返回包含2个零,3个1和1个(例如(0, 1, 0, 0, 1, 2, 1))的所有可能元组。它通过为每个可能的元素创建部分元组,减少该元素的数量并进行递归调用来实现。这些元组的元素在这里代表你的数字(4,5和6)。

sum_numbers使用partitions来计算结果。如果你有4个 x 次,5个到 y 次,6个到 z 次,它首先考虑所有可能的组合尺寸。例如,每个具有零,具有零4和5以及一个6等,直到具有 x 4, y 5和 z 6.对于其中的每一个,它计算所有可能的分区,并使用上述元组中的值来计算每个部分结果。