Abh*_*kar 7 java algorithm math dynamic-programming
我有以下问题陈述:
给定一个数n(1 <n <10 ^ 9),该集合中数学运算的最小数量(将n除以2,将n除以3,从n减去1)可以用来转换数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);
Run Code Online (Sandbox Code Playgroud)
但我没有得到所需的输出.有人可以帮助我.
我认为特里斯坦是对的 - 你无法知道哪个操作最终会产生最短的路径,所以你必须尝试所有这些才能得到正确的答案.
通常,像这样的强力解决方案意味着计算将采用指数时间.从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→ 我在你的活动地图.你可以采取一些措施来加快速度(例如,当你找到时立即突破循环1),或者减少内存占用(例如,如果它们比任何一个都大,你可以丢弃看到的哨兵您的活动条目,或使用列表而不是地图,因为迭代的所有i值都相同),但这应该足够高效,以满足您的需要.
我已将我的解决方案移植到Java并在此处发布:
输出包含一些时间.请注意,此处链接的解决方案使用了一个可见的地图和一个活动的列表.我将链中的前一个数字存储为所看到的每个地图条目的值,这允许我在结束时重建链.在输出中,3表示除以3,2表示除以2,1表示减1.
我相信这里的问题只是计算最少的步骤数,而不是有关步骤的详细信息、如何处理它等。因此在这种情况下,下面的解决方案应该是有效且最简单的解决方案。动态规划中的自下而上方法。
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.
int res = dp[n];
delete[] dp;
return res;
}
Run Code Online (Sandbox Code Playgroud)
最简单的解决方案可能是探索所有可能性。
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;
}
Run Code Online (Sandbox Code Playgroud)
解释:
n: 否
moves:包含动作的ArrayList。(用于打印目的)
bestMove:包含找到的最小解决方案的大小的值。
memory:包含先前探索的“状态”和路径长度的 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));
}
Run Code Online (Sandbox Code Playgroud)
输出将是:
3
[1, 3, 3]
1
Run Code Online (Sandbox Code Playgroud)
相当于n-1, n/3,n/3(@Tristan的最佳解决方案)
如果我们用1000 000 000as 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
Run Code Online (Sandbox Code Playgroud)
如果我们用以下方式调用它11:
4
[1, 1, 3, 3]
1
Run Code Online (Sandbox Code Playgroud)
编辑: 如果只需要移动次数:
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);
}
Run Code Online (Sandbox Code Playgroud)
调用此方法
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));
Run Code Online (Sandbox Code Playgroud)
输出:
30
24
Run Code Online (Sandbox Code Playgroud)
足够快