1. 程式人生 > >釣魚問題-DFS全排列+模擬

釣魚問題-DFS全排列+模擬

 題目:釣魚崗位有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