钓鱼问题-DFS全排列+模拟

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Gods_magic/article/details/53706990
 题目:钓鱼岗位有N(5<=N<>=60),钓鱼人数P(1<=P<=20),总共有3个入口,3个入口位置在不同的钓鱼岗,从入口到达入口对应的钓鱼岗距离为1,从该位置往两边走,能够到达下一个钓鱼岗,距离也为1。现在每个入口有一定的人排队,求出所有入口处的人全部到达钓鱼岗最小的距离。每个钓鱼岗只能有一个人。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;

//先左还是先右对结果是有影响的,所以两次情况都要考虑到
public class Test32_1216_diaoyu {
	static int roomNum;// 港口的数量
	static int[] fishRoom;// 钓鱼港口数组
	static int[] theDoor = new int[3];// 入口的位置
	static int[] people;// 入口对应的人数
	static int step1 = 0;// 优先放右边的步数
	static int step2 = 0;// 优先放左边的步数
	static int minStep = 1000000;// 最小的步数

	public static void main(String[] args) throws FileNotFoundException {
		long s = System.currentTimeMillis();
		Scanner in = new Scanner(new FileInputStream("D:\\test\\Test1216_diaoyu.txt"));
		while (in.hasNext()) {
			roomNum = in.nextInt();
			fishRoom = new int[roomNum];
			people = new int[roomNum];
			for (int i = 0; i < 3; i++) {
				int ind = in.nextInt() - 1;
				theDoor[i] = ind;
				people[ind] = in.nextInt();
			}
			// 初始化
			minStep = 1000000;
			// 调用全排列DFS
			DFS(theDoor, 0, theDoor.length - 1);
			System.out.println(minStep);
		}
		// 程序运行时间
		long e = System.currentTimeMillis();
		System.out.println(e - s + "MS");
	}

	// 全排列入口的开放顺序
	public static void DFS(int[] theDoor, int start, int end) {
		// 迭代结束条件
		if (start == end) {// 出口--插入怎样往钓鱼港口放人的函数
			// 初始化港口数组(0代表没人,1代表已经有人)
			fishRoom = new int[roomNum];
			step1 = 0;// 初始化步数
			// 按照入口开放顺序放人(优先往右边放)
			goFishing1(theDoor);
			fishRoom = new int[roomNum];
			step2 = 0;// 初始化步数
			// 优先往右边放
			goFishing2(theDoor);
			// 找出step1和step2中较小的一个
			int min = step1 > step2 ? step2 : step1;
			// 更新最小步数
			if (min < minStep) {
				minStep = min;
			}
		} else {
			// 入口开放顺序的全排列迭代体
			for (int i = start; i <= end; i++) {
				int temp = theDoor[start];
				theDoor[start] = theDoor[i];
				theDoor[i] = temp;

				DFS(theDoor, start + 1, end);

				temp = theDoor[start];
				theDoor[start] = theDoor[i];
				theDoor[i] = temp;
			}
		}
	}

	/*
	 * ---以下为模拟入口放人各种情况。debug了好长时间,仍然认为不是最简单的办法
	 */
	// 按照入口开放顺序放人(优先往右边放)
	public static void goFishing1(int[] theDoor) {
		for (int i = 0; i < 3; i++) {// 遍历三个入口
			int doorNum = theDoor[i];// 该入口的位置
			int perNum = people[doorNum];// 该入口的人数
			letGo1(doorNum, perNum, 1, doorNum);
		}
	}

	// 按照入口开放顺序放人(优先往左边放)
	public static void goFishing2(int[] theDoor) {
		for (int i = 0; i < 3; i++) {// 遍历三个入口
			int doorNum = theDoor[i];// 该入口的位置
			int perNum = people[doorNum];// 该入口的人数
			letGo2(doorNum, perNum, 1, doorNum);
		}
	}

	// 将该入口的人放去港口(DFS先右再左)
	// temp为判断往右还是往左放的标志位;stLen为入口的位置,用来计算步数用
	public static void letGo1(int doorNum, int perNum, int temp, int stLen) {
		if (perNum != 0) {
			if (doorNum >= 0 && doorNum < roomNum) {
				if (fishRoom[doorNum] == 0) {
					fishRoom[doorNum] = 1;
					int index = (doorNum - stLen) > 0 ? (doorNum - stLen + 1) : (stLen - doorNum + 1);// 每放一个人的步数
					step1 = step1 + index;// 累计步数
					perNum--;// 等待人数减少
					// doorNum <roomNum- 1防止数组越界
					if (temp % 2 == 1 && doorNum < roomNum) {
						letGo1(doorNum + temp, perNum, temp + 1, stLen);
					} else {
						letGo1(doorNum - temp, perNum, temp + 1, stLen);
					}
				} else {// 如果最近的钓鱼口已经有人在,接着往下找
					if (temp % 2 == 1 && doorNum < roomNum) {
						letGo1(doorNum + temp, perNum, temp + 1, stLen);
					} else {
						letGo1(doorNum - temp, perNum, temp + 1, stLen);
					}
				}
			} else {
				if (doorNum < 0) {// 如果超出数组范围,分情况考虑
					letGo1(doorNum + temp, perNum, temp + 1, stLen);
				} else {
					letGo1(doorNum - temp, perNum, temp + 1, stLen);
				}
			}
		}
	}

	// 将该入口的人放去港口(DFS先左再右)
	// temp为判断往右还是往左放的标志位;stLen为入口的位置,用来计算步数用
	public static void letGo2(int doorNum, int perNum, int temp, int stLen) {
		if (perNum != 0) {
			if (doorNum >= 0 && doorNum < roomNum) {// 如果在数组范围内
				if (fishRoom[doorNum] == 0) {
					fishRoom[doorNum] = 1;
					int index = (doorNum - stLen) > 0 ? (doorNum - stLen + 1) : (stLen - doorNum + 1);
					step2 = step2 + index;
					perNum--;
					if (temp % 2 == 0 && doorNum < roomNum - 1) {
						letGo2(doorNum + temp, perNum, temp + 1, stLen);
					} else {
						letGo2(doorNum - temp, perNum, temp + 1, stLen);
					}
				} else {// 如果最近的钓鱼口已经有人在,接着往下找
					if (temp % 2 == 0 && doorNum < roomNum - 1) {
						letGo2(doorNum + temp, perNum, temp + 1, stLen);
					} else {
						letGo2(doorNum - temp, perNum, temp + 1, stLen);
					}
				}
			} else {// 如果超出数组范围,分情况考虑
				if (doorNum < 0) {
					letGo2(doorNum + temp, perNum, temp + 1, stLen);
				} else {
					letGo2(doorNum - temp, perNum, temp + 1, stLen);
				}
			}
		}
	}
}
case:
10
2 4
3 2
10 3
20
3 10
6 2
13 2
11
10 6
3 3
6 2
10
10 3
1 3
5 3
10
4 5
6 2
10 2

猜你喜欢

转载自blog.csdn.net/Gods_magic/article/details/53706990