猴子搬香蕉(算法入门题目005)

题目

A 地有一堆香蕉,共 w 只。
一只猴子要把香蕉从 A 地运输到 B 地。
两地相距 s 里。
猴子每次最多只能背 c 只香蕉。
无论背多少香蕉(甚至不背),猴子每走 1 里路,会吃掉 1 只香蕉。
当然,猴子在完成运输任务后,并不需要返回 A 地。
猴子只在整数里程点往返。
求: 在使用最佳策略时,最多能运多少香蕉到 B 地。

比如: w=30, s=3, c=10,
则,最多可运去 17 只香蕉。
考虑一搬性,w s c 都可能很大,比如:
w=987654321, s=32345678, c=10000000
(这只是个数学问题,不要说猴子背不动什么的…)
输入数据保证都是大于 0 的。

分析

首先,如果 c >= w,所有香蕉一次就可运完,则为简单情况。
更多的情况是需要往返运输。则要考虑怎样才节省。
显然,此时,猴子把香蕉一里一里地运输总是最优的。
因此,可得最笨的解法。
使用这种解法要注意一个陷阱,那就是零头的处理问题。
在此基础下,可以分析出哪些步骤可以合并,则得更优解法。

这个问题有 3 个变量,在考虑边界条件的时候要小心一些。
这 3 个变量的地位不一样,在给定一个具体问题时,
c 变成常量,而 w, s 随着求解过程不断变化。

代码

先来个笨的,较可靠的:

//problem005
public class A
{
	// 保证输入数据大于0
	static int f(int w, int s, int c){
		if(c < 2) return 0;
		if(c == 2){
			if(s > 1) return 0;
			if(w < 2) return 0;
			return 1;
		}
		if(w <= s) return 0;
		if(c > w) return w-s;
		
		while(s > 0){
			if(w < s) {
				w = 0;
				break;
			}
			if(w%c <= 2) w -= w%c;
			w -= (w-1) / c * 2 + 1;
			s--;
		}
		
		return w;
	}
	
	static void test(int w, int s, int c){
		System.out.println(String.format("w=%d s=%d c=%d --> %d", w, s, c, f(w,s,c)));
	}
	
	public static void main(String[] args){
		test(30,3,10);
		test(30,6,10);
		test(3000,1000,1000);
		test(505,497,1000);
		test(987654321,32345678,10000000);
		test(30,1,2);
		test(30,10,2);
		test(30,40,10);
		test(30,28,100);
	}
}

进行必要的合并后,得到的更优的解法:

//problem005
public class B
{
	static int f(int w, int s, int c){
		if(c < 2) return 0;
		if(c == 2){
			if(s > 1) return 0;
			if(w < 2) return 0;
			return 1;
		}
		if(w <= s) return 0;
		if(c > w) return w-s;
		
		while(s>0){
			if(w <= s) return 0;
			
			if(w%c <= 2) w -= w%c;
			int n = (w-1)/c;
			int u = w - n * c;
			int dw = n * 2 + 1; 
			
			int m = (u-2+dw-1)/dw;
			if(s<m) m = s;
			
			w -= m * dw;
			s -= m;			
		}
		
		return w;
	}
	
	static void test(int w, int s, int c){
		System.out.println(String.format("w=%d s=%d c=%d --> %d", w, s, c, f(w,s,c)));
	}
	
	public static void main(String[] args){
		test(30,3,10);
		test(30,6,10);
		test(3000,1000,1000);
		test(505,497,1000);
		test(987654321,32345678,10000000);
		test(30,1,2);
		test(30,10,2);
		test(30,40,10);
		test(30,28,100);
		test(100,50,20);
	}
}

详解

这个小题目更多地是练习数学,练习整数与整除,余数等的性质。
用小的数据跟随程序上的公式逐步跟踪一下。
如果还是不能理解,可参考笔者在“千聊”上的同样题目的详解。
或者,手机扫下图加关注。

你可能感兴趣的:(猴子搬香蕉(算法入门题目005))