我正在考虑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
有意义吗?从性能的角度来看它是最优的吗?
...
好吧,现在我看到动态编程解决方案会更有效率。然而,如果所提出的解决方案仍然有意义,那就很有意思了。
答案 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]