我在一个挑战中发现了这个问题。
问题是我们得到一个整数N,我们需要找到数字乘积为N
的最小数字X.我的方法是用单个数字查找素数 例如10倍数是2.5 对于100倍数是2,2,5,5
你们能想到更好的算法吗?
int getNumber(int n) {
int temp = n;
int a[4] ={2,3,5,7};
std::string str;
do
{
for(int i=0;i<4;++i)
{
int val = temp%a[i];
if(val ==0)
{
char s[260];
sprintf(s,"%d",a[i]);
str += s;
temp /=a[i];
break;
}
}
}while(temp>1);
char newStr[260];
int countof2=0,countof3=0;
for(int i=0;i<str.length();++i)
{
if(str[i]=='2')
++countof2;
if(str[i]=='3')
++countof3;
}
bool bFlag=false;
int indexNew=0;
if(countof2 >= 3)
{
int count =0;
for(int index=0;index<str.length();++index)
{
if(str[index]=='2'&& count<3)
++count;
else
{
newStr[indexNew]= str[index];
++indexNew;
}
}
newStr[indexNew]='8';
bFlag=true;
}
else if(countof2 >=2)
{
newStr[indexNew++]='4';
int count =0;
for(int index=0;index<str.length();++index)
{
if(str[index]=='2'&& count<2)
++count;
else
{
newStr[indexNew]= str[index];
++indexNew;
}
}
bFlag=true;
}
else if(countof3 >=2)
{
int count =0;
for(int index=0;index<str.length();++index)
{
if(str[index]=='3'&& count<2)
++count;
else
{
newStr[indexNew]= str[index];
++indexNew;
}
}
newStr[indexNew]='9';
bFlag=true;
}
newStr[++indexNew]= '\0';
int val=0;
if(bFlag)
val = atoi(newStr);
else
val = atoi(str.c_str());
return val;
}
答案 0 :(得分:3)
我没有用(2,2,2) - > 8,(3,3) - > 9等寻找奇怪的解决方案,而是采用直接的解决方案(忘记素数,直接转到所有位):
for (d = 9; d > 1; d--)
while (n % d == 0) {
n /= d;
list.addDigit(d);
}
if (n != 1) print("no such number"); else print(reverse(list));
答案 1 :(得分:1)
如果存在D
的素数除数N such that D>=10
,则无法解决。
你的方法几乎是正确的。唯一的评论是6 = 2*3
最优策略是将许多数字转换为一个数字,如2*2*2->8
,如果存在许多变换变体,请选择那些给出最小数字的变体。
按以下顺序执行以下步骤:
1)将所有(2,2,2)三元组转换为8。
2)(2,2) - > 4
3)(2,3) - > 6
4)(3,3) - > 9
将所有数字放入一个数组并对其进行排序。请注意,您还必须输入结果数组,如5,7,剩余的2和3。
输出排序数组 - 它将回答您的问题。
示例强> N = 96 = 2 * 2 * 2 * 2 * 2 * 3 = 8 * 4 * 3。排序(8,4,3) - 3,4,8。答案是348。
答案 2 :(得分:0)
假设D = N的所有除数(不是必要的素数)的集合。然后可以计算dp(d) - 数字的最小长度,数字乘积恰好d为D中的所有d。重建答案,一可以找到最小的x,例如dp(d / x)= dp(x)-1,从d = N开始,直到d = 1.该算法产生最短和最小的lexigraph数。因此,这是正确的。 上面答案中描述的贪婪算法是不正确的。它产生348,N = 96,而正确的答案是268。 这是我的代码。
import java.util.*;
public class Main {
static Map<Integer, Integer> dp;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
System.out.println(getNumber(N));
}
static int getNumber(int N) {
if (N == 1)
return 1;
dp = new HashMap<>();
if (getDp(N) == Integer.MAX_VALUE)
return -1;
String result = reconstruct(N);
return Integer.parseInt(result);
}
static int getDp(int N) {
if (N == 1)
return 0;
if (dp.containsKey(N))
return dp.get(N);
int best = Integer.MAX_VALUE;
for (int digit = 2; digit <= 9; digit++)
if (N % digit == 0)
best = Math.min(best, getDp(N / digit) + 1);
dp.put(N, best);
return best;
}
static String reconstruct(int N) {
if (N == 1)
return "";
int minDigit = 2;
while (N % minDigit != 0 || getDp(N / minDigit) + 1 != getDp(N))
minDigit++;
String result = reconstruct(N / minDigit);
return Integer.toString(minDigit) + result;
}
}
答案 3 :(得分:0)
查找素数除数及其{2,3,5,7}
的幂。如果在连续除法后仍然保持大于1,那么就没有解,因为余数是素数而不能由{1-9}
的乘法得出。现在唯一剩下的就是结合2&amp; 3这样的组合是最小的。
通过以下方式找到最低要求: -
1. Combine all k (2,2,2) => k 8's
2. all m (2,2) => m 4's
3. all x (2,3) => x 6's
4. all y (3,3) => y 9's
使用排序在O(logN)
中获得最小解,其中N是输入数。
答案 4 :(得分:0)
这里的解决方案呈指数级增长,但很容易使用动态编程技术成为伪多项式。
我将从一个递归开始,我们将从最小可能的除数开始并分成子问题。它将返回最终除法的值并保存最小值。
编辑:只是添加它只适用于非素数输入
int getMinDigit(int N, int P) {
if(N == 1) {
return P;
}
int div = 2;
int min = INT_MAX;
do {
//checking if its divisible
if(N%div == 0) {
//get the other multiplier
int Q = N/div;
//we are done with our recursion
if(Q < div)
break;
//getting the sub problem resolved
int value = getMinDigit(Q, 10*P + div);
min = min<value? min: value;
}
div++;
} while(true);
return min;
}
此算法的时间复杂度是指数级的。我们可以使用DP减少伪多项式的时间复杂度。
简单地以自下而上的方式计算子问题,或者每次调用memonize解决方案。像P(6)P(9)
P(36)
|
P(2, P(18))
| |
| P(2, P(9)) (matching sub problem)
| P(3, P(6))
P(3, P(12))
| |
| P(2, P(6))
| P(3, P(4))
P(4, P(9)) <-- this is your solution
| |
| P(3, P(3));
P(6, P(6))
| |
| P(2, P(3))
(x) break;
P(9, P(4))