我有以下问题陈述:
给定数n(1
到目前为止,我已经编写了以下代码以尝试解决问题:
while(n!=1){
if(n%3==0 || n%2==0){
if(n%3==0){
n=n/3;
c=c+1;
}
if(n%2==0){
n=n/2;
c=c+1;
}
}
else{
n=n-1;
c=c+1;
}
}
System.out.println(c);
但我没有得到所需的输出。有人可以帮助我。
答案 0 :(得分:7)
我认为特里斯坦是对的 - 你无法知道哪个操作最终会产生最短的路径,所以你必须尝试所有这些才能得到正确的答案。
通常,像这样的强力解决方案意味着计算将采用指数时间。您从 n 开始,然后使用3个操作计算最多3个新数字。然后对于这3个数字中的每一个,你得到另外3个,共9个。然后对于这9个中的每一个,你得到另外3个,总共27个;等等。你可以看到如何快速结束一系列荒谬的可能性。
但是,此处的搜索空间有限。由于您的所有操作都会导致值减少,因此您只会遇到从1到 n 的数字,包括1和 n 。这意味着它最多需要 n 操作才能达到你的目标1.每个数字只有一条最短路径,所以一旦你找到了这条路,你就不需要考虑任何您找到的其他路径会导致相同的数字。如果你保留一组以前看过的数字,你应该能够消除大部分的搜索空间,因为你可以抛弃重复的结果。 (这是Memoization的一种形式。)
以下是我将如何解决这个问题:
Set<Integer>
以包含以前看到的值。Map<Integer, Integer>
以保留有效值。每个键→值条目的键将是从 n 到1
的路径中的数字,该值将是达到该值时所需的操作数号。0
放入有效地图。1
的键(您的目标)时:
1
→ i 返回值 i 。您可以采取一些措施来加快速度(例如,当您找到1
时立即突破循环),或者减少内存占用(例如,您从所看到的集合中丢弃哨兵)如果它们比你的任何活动条目都大,或者使用列表而不是地图,因为迭代的所有 i 值都是相同的),但这应该足够有效地做你做的事情需要。
我已将我的解决方案移植到Java并在此处发布:
输出包含一些时间。请注意,此处链接的解决方案使用看到的地图和活动的列表。我将链中的前一个数字存储为中看到的每个地图条目的值,这样我就可以在最后重建链。在输出中,3表示除以3 ,2表示除以2 ,1表示减去1 。
答案 1 :(得分:2)
这是一个值得思考的有趣案例。 从10开始,你有几个选择:
10 / 2 = 5 1 move
5 - 1 = 4 2 moves
4 / 2 = 2 3 moves
2 - 1 = 1 4 moves
10 - 1 = 9 1 move
9 / 3 = 3 2 moves
3 / 3 = 1 3 moves
距离被2整除的数字怎么样? 从11开始,我们有以下选择:
11 - 1 = 10 1 move
10 / 2 = 5 2 moves
5 - 1 = 4 3 moves
4 / 2 = 2 4 moves
2 / 2 = 1 5 moves
11 - 1 = 10 1 move
10 - 1 = 9 2 moves
9 / 3 = 3 3 moves
3 / 3 = 1 4 moves
也许这只有在你减去的数字是否可以被3整除时才有效?谁知道,好运OP。
答案 2 :(得分:2)
最简单的解决方案可能是探索所有可能性。
public static ArrayList<Integer> solve(int n,
ArrayList<Integer> moves, int bestMove,HashMap<Integer,Integer> memory) {
if (moves.size() >= bestMove) return null;
if (n == 1) return moves;
Integer sizeOfPathN= memory.get(n);
if (sizeOfPathN!=null && sizeOfPathN<=moves.size())return null;
memory.put(n,moves.size());
int size_1=Integer.MAX_VALUE, size_2 = Integer.MAX_VALUE, size_3 = Integer.MAX_VALUE;
ArrayList<Integer> moves3 = null, moves2 = null, moves1;
if (n % 3 == 0) {
ArrayList<Integer> c = new ArrayList<Integer>(moves);
c.add(3);
moves3 = solve(n / 3, c,bestMove,memory);
if (moves3!=null)
size_3 = moves3.size();
}
bestMove = Math.min(bestMove, size_3);
if (n % 2 == 0) {
ArrayList<Integer> c = new ArrayList<Integer>(moves);
c.add(2);
moves2 = solve(n / 2, c,bestMove,memory);
if (moves2!=null)
size_2 = moves2.size();
}
bestMove = Math.min(bestMove, size_2);
ArrayList<Integer> c = new ArrayList<Integer>(moves);
c.add(1);
moves1 = solve(n - 1, c,bestMove,memory);
if (moves1!=null)
size_1 = moves1.size();
int r = Math.min(Math.min(size_1, size_2),size_3);
if (r==size_1) return moves1;
if (r==size_2) return moves2;
return moves3;
}
<强>解释强>:
n
:n
moves
:包含运动的ArrayList。 (用于打印pourposes)
bestMove
:包含找到的最小解决方案大小的值。
memory
:包含&#34;状态&#34;的HashMap先前探讨了路径的长度。
如果我们打电话 public static void main(String [] args){
long a = System.currentTimeMillis();
Object[] sol=solve(10, new ArrayList<Integer>(),Integer.MAX_VALUE,new HashMap<Integer,Integer>()).toArray();
System.out.println(sol.length);
System.out.println(Arrays.toString(sol));
System.out.println((System.currentTimeMillis()-a));
}
输出结果为:
3
[1, 3, 3]
1
相当于n-1, n/3,n/3
(@Tristan的最佳解决方案)
如果我们将1000 000 000
称为n:
30
[1, 3, 3, 3, 3, 1, 3, 3, 1, 3, 1, 1, 3, 3, 3, 3, 1, 2, 2, 1, 3, 2, 1, 3, 3, 2, 1, 3, 2, 2]
55
如果我们使用11
调用它:
4
[1, 1, 3, 3]
1
修改强> 如果只需要移动次数:
public static int solve(int n,int moves,int bestMove,HashMap<Integer,Integer> memory) {
if (moves >= bestMove) return Integer.MAX_VALUE;
if (n == 1) return moves;
Integer sizeOfPathN= memory.get(n);
if (sizeOfPathN!=null && sizeOfPathN<=moves)return Integer.MAX_VALUE;
memory.put(n,moves);
int size_1=Integer.MAX_VALUE;
int size_2 = Integer.MAX_VALUE;
int size_3 = Integer.MAX_VALUE;
moves=moves+1;
if (n % 3 == 0) size_3 = solve(n / 3, moves,bestMove,memory);
bestMove = Math.min(bestMove, size_3);
if (n % 2 == 0) size_2=solve(n >> 1, moves,bestMove,memory);
bestMove = Math.min(bestMove, size_2);
size_1 = solve(n - 1, moves,bestMove,memory);
return Math.min(Math.min(size_1, size_2),size_3);
}
使用
调用此方法long a = System.currentTimeMillis();
System.out.println(
solve(1000 *1000*1000, 0,Integer.MAX_VALUE,new HashMap<Integer,Integer>()));
System.out.println((System.currentTimeMillis()-a));
输出:
30
24
足够快
答案 3 :(得分:2)
我认为这里的问题是计算最小步数,而不是关于步骤的详细信息,如何处理它等等。因此,在这种情况下,解决方案应该是高效且最简单的。动态编程中的自下而上的方法。
int getMinOperations(int n) {
// Use this array to store the previous solved result.
int dp[] = new int[n + 1];
// base case, if it is 1, we do not need to do any processing
dp[1] = 0;
for (int i = 2; i <= n; i++) {
// for the time being, let's assume we are getting minimum number of step by subtracting 1
dp[i] = 1 + dp[i - 1];
// if number if divisible by 2 let's divide it and compare it with the number of steps
// calculated in previous step, choose the minimum one
if (i % 2 == 0)
dp[i] = Math.min(dp[i], 1 + dp[i / 2]);
// if number if divisible by 3 let's divide it and compare it with the number of steps
// calculated in previous step, choose the minimum one
if (i % 3 == 0)
dp[i] = Math.min(dp[i], 1 + dp[i / 3]);
// At this step we have stored the minimum step to reduce the i to 1. and we will continue till nth value
}
// Returning nth value of array, as value at each index is the minimum number of steps to reduce it to 1.
return dp[n];
}
答案 4 :(得分:0)
注意:这不是实际代码,你需要考虑极端情况,这是一个伪代码,它给出了关于算法的逻辑。
这可以递归完成,但由于n
非常高,你需要记住使用额外的空间。
fun(int n)
{
if(n==0) return INFINITY;
if(n==1) return 0;
if(n%3==0)
a = fun(n/3)
if(n%2==0)
b = fun(n/2)
return min(fun(n-1) , a+1 , b+1)
}
答案 5 :(得分:0)
使用动态编程的两种解决方案的Python实现
1)自上而下
def calculate(mins, n):
if n<= 0:
raise ValueError("input is not positive")
if n == 1:
return 0
if (mins[n] != -1):
return mins[n]
result = 1 + calculate(mins, n-1)
if (n % 2 == 0):
result = min(result, 1 + calculate(mins, n // 2))
if (n % 3 == 0):
result = min(result, 1 + calculate(mins, n // 3))
mins[n] = result
return result
def get_min_step_memoization (n):
mins = [-1] * (n+1)
return calculate(mins, n)
2)自下而上
def get_min_step_dp(n):
if n<= 0:
raise ValueError("input is not positive")
results = [-1] * (n+1)
results[1] = 0
for idx in range(2, n+1):
results[idx] = 1 + results[idx-1]
if (idx % 2 == 0):
results[idx] = min(results[idx], 1 + results[idx//2])
if (idx %3 == 0):
results[idx] = min(results[idx], 1 + results[idx//3])
return results[n]
答案 6 :(得分:0)
使用记忆速度。
初始化你的地图:
n_to_steps = {1=>0, 2=>1, 3=>1}
现在计算。不要计算从1到n的所有内容......而是:
def f(n)
if n isn't a key in our map
n_to_steps[n] = min(1 + n%2 + f(n/2), 1 + n%3 + f(n/3))
end
return n_to_steps[n]
end
答案 7 :(得分:-1)
我认为n= 10^9
指向一些逻辑,不只是尝试所有可能的情况和所有排列
这是我得到的:
1 -> 0
2 -> 1
3 -> 1
4 -> 2
5 -> 3
6 -> 2
7 -> 3
8 -> 3
9 -> 2
10 ->3
11 ->4
12 ->3
13 ->4
14 ->4
15 ->3
16 ->4
17 ->5
18 ->3
19 ->4
20 ->4
21 ->4
所以我认为fornula是:
while(n!=1)
{
if(n%3==0)
ans += 1 and n/=3
else if (n%2==0)
ans += 1 and n/=2
else n-- , ans+=1
}
在0.06秒内为N = 1000000000提供答案。 http://ideone.com/0pfKoz
// 以前不正确的逻辑(对于@Dukeling指出的n = 30很容易失败)
while(n!=1)
{
if(n%2==0 && n%3==0)
ans += p1 + p2 and n=1 {where n = 2^p1 * 3^p2 }
else if (n%2!=0 && n%3 !=0)
n--
else if (n%2==0 && n%3 !=0)
ans+=1 and n/=2
else if (n%2!=0 && n%3 ==0)
ans+=1 and n/=3
}
请澄清您是否已获得此问题的答案,并尝试查看我的逻辑是否有效。