过河问题的递归解法

好久没写算法的问题了,感觉退步了很多。写了一道过河的问题,想了一下午用深度遍历憋出来了,结果复杂度有点多,以后再试试别的解法吧。

题目大意:

野人和传教士过河,野人传教士的数目相同,船的负载为M(1<M<=5),野人传教士全会划船,若野人数量大于传教士则会吃掉传教士,求划船的最小次数。

输入格式 :

第一行:为测试的个数

第二行:野人和传教士的数目N, 船的负载M

例如:

3
3 2
4 3
5 2

思想:

设A->B岸运输,即A为出发地点,B为终点

1  A点,B点船上的人数要求都是 传教士大于野人,除非没有传教士

2 若将出发地做标记,则出发地为人数为0,成功,或者若出发地人数小于船的负载且靠A岸则直接可以+1后成功。

3 A->B时 船上的人数要大于1, B->A无限制

4 以A岸人数为代表,若人数重复则此次无效。注意此题船在A岸和B岸要分开考虑,人数相同但船在A岸和B岸是不同情况 

import java.util.Scanner;

public class Algorithm {
	static int Answer;
	static int buffer[][] = new int[25][4];
	static int manCount;//记录人数

	public static void main(String args[]) throws Exception	{

		Scanner sc = new Scanner(System.in);
		int T = sc.nextInt();
		for(int test_case = 0; test_case < T; test_case++) {
			int man = sc.nextInt();// 传教士和野人的人数
			int boat = sc.nextInt();//船的负载	
			manCount = man;
			int count = 0;//船行驶即时的数目,用于递归传值
			Answer = 9999; //取最优值,故初始化一个大的值
			
			function(man, man , 0, 0, boat, count);
			System.out.println("Case #"+(test_case+1));
			if (Answer != 9999) {
				System.out.println(Answer);
			} else {
				System.out.println("impossible");
			}
		}
	}
	
	private static void function(int man, int eman, int bankman, int bankeman, int boat, int count) {
		if (count > 17) {
			return ;
		}
		if ((man < eman && man!=0 && eman!=0) || (bankman < bankeman && bankman!=0 && bankeman!=0)) {//野人大于传教士的情况
			return ;
		}
		count++;
		int k;
		if (count%2 == 1) { //count为单数时,为A->B的情况
			if (man+eman <= boat) {//若A岸上的人数小于船的负载,则可直接记录为一个解
				if (Answer > count)//成功但不一定是最优解
					Answer = count;//递归完成后取最小值为最优解
				return;
			}
			for ( k = 0; k < 25; k++) {//此时为检查有没有重复以前的情况
				if (buffer[k][0] == 0 && buffer[k][1] == 0) {
					break;
				}
				if (buffer[k][0] == man && buffer[k][1] == eman) {
					return ;
				}
			}
			buffer[k][0] = man;
			buffer[k][1] = eman;
			for (int i = boat; i >= 0; i--) {//man
				for (int j = boat-i; j >= 0; j--) {//eat man
					if (i+j > 1 && i+j <= boat && (i >= j || i==0 || j==0)&& !(i==0&&j==0)
							&& man-i >= 0 && eman-j >= 0) {//i为传教士j为野人
						int newman = man - i;
						int neweman = eman - j;
						int bnewman = manCount-newman;
						int bneweman = manCount-neweman;
						if (newman>=0 && neweman>=0 && bnewman>=0 && bneweman>=0) {
							function(newman,neweman,bnewman,bneweman,boat,count);
						}
					}
				}
			}
			buffer[k][0] = 0;
			buffer[k][1] = 0;
		} else {//B->A的情况
			for ( k = 0; k < 25; k++) {
				if (buffer[k][2] == 0 && buffer[k][3] == 0) {
					break;
				}
				if (buffer[k][2] == man && buffer[k][3] == eman) {
					return;
				}
			}
			buffer[k][2] = man;
			buffer[k][3] = eman;
			//回A岸
			for (int i = 0; i <= boat; i++) {//man
				for (int j = 0; j <= boat-i; j++) {//eat man
					if (i+j <= boat && (i >= j || i==0 || j==0)&& !(i==0&&j==0)
							&& bankman-i >= 0 && bankeman-j >= 0) {
						int newman = bankman - i;
						int neweman = bankeman - j;
						int bnewman = manCount-newman;
						int bneweman = manCount-neweman;
						if (newman>=0 && neweman>=0 && bnewman>=0 && bneweman>=0) {
							function(bnewman,bneweman,newman,neweman,boat,count);
						}
					}
				}
			}
			buffer[k][2] = 0;
			buffer[k][3] = 0;
		}
		return;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值