Old Top Coder谜语:通过插入+来制作一个数字

时间:2011-11-26 18:37:55

标签: algorithm combinatorics

我正在考虑this topcoder problem

  

给定一串数字,找到字符串所需的最小加法数等于某个目标数。每次添加相当于在数字串中的某处插入加号。插入所有加号后,像往常一样评估总和。

     

例如,考虑“303”和目标总和6.最佳策略是“3 + 03”。

我会用蛮力解决它:


for each i in 0 to 9 // i -- number of plus signs to insert
  for each combination c of i from 10
    for each pos in c // we can just split the string w/o inserting plus signs
      insert plus sign in position pos 
    evaluate the expression
    if the expression value == given sum
      return i

有意义吗?从性能的角度来看它是最优的吗?

...

好吧,现在我看到动态编程解决方案会更有效率。然而,如果所提出的解决方案仍然有意义,那就很有意思了。

6 个答案:

答案 0 :(得分:4)

这当然不是最佳的。例如,如果您获得字符串“1234567890”并且目标是三位数字,则您知道必须将字符串拆分为至少四个部分,因此您无需检查0,1或2个插入。此外,目标限制了允许插入位置的范围。这两点对短弦都有很小的影响,但对于较长的弦可以产生巨大的影响。但是,我怀疑有一个非常好的方法,闻起来有点DP。

答案 1 :(得分:3)

我还没有多想过,但是如果向下滚动,你可以看到它所来自的比赛链接,从那里你可以看到解算器的解决方案。这是C#中的一个。

using System; 
using System.Text; 
using System.Text.RegularExpressions; 
using System.Collections; 

public class QuickSums { 
    public int minSums(string numbers, int sum) { 
        int[] arr = new int[numbers.Length]; 
    for (int i = 0 ; i < arr.Length; i++)       
      arr[i] = 0; 

    int min = 15; 

    while (arr[arr.Length - 1] != 2)     
    { 
      arr[0]++; 
      for (int i = 0; i < arr.Length - 1; i++) 
        if (arr[i] == 2)  
        { 
          arr[i] = 0; 
          arr[i + 1]++; 
        } 

      String newString = ""; 
      for (int i = 0; i < numbers.Length; i++) 
      { 
        newString+=numbers[i]; 
        if (arr[i] == 1) 
          newString+="+"; 
      } 

      String[] nums = newString.Split('+'); 
      int sum1 = 0; 
      for (int i = 0; i < nums.Length; i++) 
        try  
        { 
          sum1 += Int32.Parse(nums[i]); 
        } 
        catch 
        { 
        } 

      if (sum == sum1 && nums.Length - 1 < min) 
        min = nums.Length - 1; 
    } 

    if (min == 15) 
      return -1; 

    return min; 
  } 

} 

答案 2 :(得分:1)

因为输入长度很小(10)所有可能的方式(可以通过长度为10的简单二进制计数器找到)很小(2 ^ 10 = 1024),所以你的算法足够快并且返回有效的结果,并且IMO没有必要改进它。

总之,在您的解决方案在时间和内存以及其他给定约束下正常工作之前,不需要进行微优化。例如,所提供的akappa这种情况可以通过DP在DP-two分区问题中解决,但是当你的算法很快时,就没有必要这样做,可能会增加一些大常量或使代码不可读。

我只提供一次字符串的解析数字(长度为10的数组)以防止过多的字符串解析,并且只使用* 10 ^ k + ...(也可以为k =计算10 ^ k在启动时0..9并保存其值)。

答案 3 :(得分:1)

我认为问题类似于矩阵链乘法问题,我们必须将括号放在最小乘法上。这里大括号代表'+'。所以我认为它可以通过类似的dp方法解决..将尝试实现它。

答案 4 :(得分:1)

动态编程:

public class QuickSums {

public static int req(int n, int[] digits, int sum) {

    if (n == 0) {
        if (sum == 0)
            return 0;
        else
            return -1;
    } else if (n == 1) {
        if (sum == digits[0]) {
            return 0;
        } else {
            return -1;
        }
    }

    int deg = 1;
    int red = 0;

    int opt = 100000;
    int split = -1;

    for (int i=0; i<n;i++) {
        red += digits[n-i-1] * deg;

        int t = req(n-i-1,digits,sum - red);
        if (t != -1 && t <= opt) {
            opt = t;
            split = i;
        }
        deg = deg*10;
    }

    if (opt == 100000) 
        return -1;
    if (split == n-1) 
        return opt;
    else
        return opt + 1;

}

public static int solve (String digits,int sum) {
   int [] dig = new int[digits.length()];
   for (int i=0;i<digits.length();i++) {
       dig[i] = digits.charAt(i) - 48;
   }

    return req(digits.length(), dig, sum);
}


public static void doit() {
    String digits = "9230560001";
    int sum = 71;

    int result = solve(digits, sum);
    System.out.println(result);
}

答案 5 :(得分:1)

似乎为时已晚......但只是阅读了一些评论和答案,其中对dp方法说不。但它是一个非常直接的dp,类似于切杆问题:

获得本质:

int val[N][N];
int dp[N][T];

val[i][j]: numerical value of s[i..j] including both i and j

val[i][j] can be easily computed using dynamic programming approach in O(N^2) time

dp[i][j] : Minimum no of '+' symbols to be inserted in s[0..i] to get the required sum j

dp[i][j] = min( 1+dp[k][j-val[k+1][j]] ) over all k such that 0<=k<=i and val[k][j]>0

简单来说,要计算dp [i] [j],你假设最后一个'+'符号的位置k,然后重复s [0..k]