当前位置:首页 » Project Euler » 详细页

Efficient exponentiation 题号:122 难度: 40 中英对照

The most naive way of computing n15 requires fourteen multiplications:

n × n × ... × n = n15

But using a "binary" method you can compute it in six multiplications:

n × n = n2
n2 × n2 = n4
n4 × n4 = n8
n8 × n4 = n12
n12 × n2 = n14
n14 × n = n15

However it is yet possible to compute it in only five multiplications:

n × n = n2
n2 × n = n3
n3 × n3 = n6
n6 × n6 = n12
n12 × n3 = n15

We shall define m(k) to be the minimum number of multiplications to compute nk; for example m(15) = 5.

For 1 ≤ k ≤ 200, find m(k).

Solution

首先对于$2^k$,明显的可以看出最快的方式是:$1->2->4->8->...->2^k$. 对于限制$limit$,当按照上述方式得到第一个$2^k \geq limit$时,可以开始回溯。 对于$limit=10$的情况,举例如下: 1. 首先生成一条链$1->2->4->8->16$,注意到$16$超过了限制,开始回溯到$8$。 2. 拓展$8$:在$8$下方继续增加$(8+4),(8+2),(8+1)$,得到:$1->2->4->8->(16,12,10,9)$,此时$9\leq limit$,则开始拓展$9$: 3. 拓展$9$:在$9$下方继续增加$(9+8),(9+4),(9+2),(9+1)$,注意到增加的都是从$9$到根$1$的所有值。此时$9+1=10>limit$,于是回溯到$8$,$8$所有可能的子节点已经被枚举尽,回溯到$4$。 4. 拓展$4$:在$4$下方增加$(4+2)$,此时$4+2=6\leq limit$,是一个可拓展的节点,所以继续拓展$6$。 5. 拓展$6$:$6$下方增加$(6+4),(6+2)$,注意到$6+2=8\leq limit$,$8$应当是一个可拓展的节点,但是注意到已经枚举过$8$,并且$8$的深度(到$1$的路径长度)比当前节点小,所以不拓展此处的$8$。 6. 继续拓展$6$:在$6$下方增加$(6+1)$,继续拓展$7$。 7. ... 依次下去,最后构成一颗树,树中出现了$1,2,...,limit$所有数,其中任意一个节点到$1$的路径长度即为题目所求的最少乘法操作次数。 对同一个数$i$,如果在树中出现多次,则取深度最小的(距离$1$最近的)作为其答案$m(i)$即可。

Code



public final class p122 {
    public static void main(String[] args) {
        long start=System.nanoTime();
        String result = run();        
        long end=System.nanoTime();
        System.out.println(result);
        System.out.println( (end-start)/1000000 + "ms" );
    }	
    static final int limit=200;
    static int m[]=new int[limit+1];//答案
    static int data[]=new int[limit+1];//树上的节点
    
    static public String run(){
    	for(int i=0;i<limit+1;i++)
    		m[i]=Integer.MAX_VALUE;
    	solve(1,0);//把1放在树根上,其深度为0
    	int ans=0;
    	for(int i=1;i<=limit;i++){
    		ans+=m[i];
    	}
    	return ""+ans;
	}
    
    static public void solve(int x, int depth) {
        if (x > limit || depth > m[x]) return;
        m[x] = depth;
        data[depth] = x;
        for (int i = depth; i >= 0; i--)
            solve(x + data[i], depth + 1);
    }
    
    
    
	
}
1582
6ms