1. 程式人生 > >程式設計之美之小飛的電梯排程演算法(多種解法)---Java語言

程式設計之美之小飛的電梯排程演算法(多種解法)---Java語言

1.題目情景

        我們假設都是從一樓上電梯的,而至於訊電梯停在其中的某一層。即所有的乘客都從一樓上電梯,到達某層之後,電梯停下來,所有乘客再從這裡爬樓梯到自己的目的層。在一樓的時候,每個乘客選擇自己的目的層,電梯則自動計算出應停的樓層,並且能夠保證該層停使得所有乘客爬樓梯的層數之和最少。 輸入:第一行樓層數N,乘客人數n;第二行有n個數,表示乘客選擇的目的層。 輸出:輸出為停止樓層和總共需要爬的樓梯。

2.解法一

        第一種方法就是採取暴力求解的辦法,採用雙重迴圈的方式,外迴圈控制停的樓層,內迴圈計算當前樓層停,總共需要爬多少樓層,並保留最小值及其相應停電梯的樓層。 具體程式碼如下:
package bianchengzhimei.part1_8;
import java.util.*;
public class ElevatorController {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int N=sc.nextInt();
		int n=sc.nextInt();
		int passenger[]=new int[n];
		for(int i=0;i<n;i++)
		{
			passenger[i]=sc.nextInt();
		}
		int stopFloor=0;
		int minFloor=Integer.MAX_VALUE;
		for(int i=1;i<=N;i++)//控制停電梯的樓層
		{
			int temp=0;
			for(int j=0;j<n;j++)//計算停在當前樓層,總共需要爬的樓層
			{
				temp+=Math.abs(passenger[j]-i);
			}
			if(temp<minFloor)
			{
				minFloor=temp;
				stopFloor=i;
			}
		}
		System.out.println("最佳停電梯的樓層為:"+stopFloor);
		System.out.println("乘客需要爬樓梯的總數為:"+minFloor);
		sc.close();

	}

}
執行結果如下:
10 6
4 8 3 9 10 5
最佳停電梯的樓層為:5
乘客需要爬樓梯的總數為:15
        這種方法比較簡單直接,因此複雜度較高,為O(N*n),所以接下來我們想辦法降低複雜度。如果n大於N,那麼可以將上述程式做一些修改也可以稍微降低複雜度。當n大於N時,先計算N層樓每層樓有幾個乘客,複雜度為O(n),然後再遍歷,遍歷過程複雜度為O(N*N),因此下面程式總複雜度為O(n+N*N)。
package bianchengzhimei.part1_8;
import java.util.*;
public class ElevatorController {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int N=sc.nextInt();
		int n=sc.nextInt();
		int passenger[]=new int[N+1];
		for(int i=0;i<n;i++)
		{
			passenger[sc.nextInt()]+=1;
		}
		int stopFloor=0;
		int maxFloor=Integer.MAX_VALUE;
		for(int i=1;i<=N;i++)//控制停電梯的樓層
		{
			int temp=0;
			for(int j=1;j<=N;j++)//計算停在當前樓層,總共需要爬的樓層
			{
				temp+=Math.abs(j-i)*passenger[j];
			}
			if(temp<maxFloor)
			{
				maxFloor=temp;
				stopFloor=i;
			}
		}
		System.out.println("最佳停電梯的樓層為:"+stopFloor);
		System.out.println("乘客需要爬樓梯的總數為:"+maxFloor);
		sc.close();

	}

}


3.解法二

        我們假設停在第i層樓,那麼當前樓層需要爬的總樓層是Y。如果有N1個乘客在樓層i以下,有N2個乘客在第i層樓,還有N3個乘客在第i層以上。那麼如果現在電梯停在i-1層,則總共需要爬Y-N1+(N2+N3)層,如果改在i+1層,那麼總共需要爬Y-N3+(N2+N1)層,所以我們可以換個思路,當電梯從一樓逐漸往上走的時候,當N2+N1-N3<0時,則繼續往上爬,噹噹N2+N1-N3>=0,則可以停下來,因為往上爬的過程中N3是逐漸減小的,一旦N2+N1-N3>=0之後,那麼隨著樓層往上爬,這個差值會越來越大,即代表人們要爬的總樓層越來越多,所以當差值不再為負即可停下來。 實現程式碼如下:
package bianchengzhimei.part1_8;

import java.util.Scanner;

public class ElevatorController2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int N=sc.nextInt();
		int n=sc.nextInt();
		int Floor[]=new int[N+1];//用來統計每個樓層停下的人數
		for(int i=0;i<n;i++)
			Floor[sc.nextInt()]+=1;
		sc.close();
		int stopFloor=1;//先從第一層開始推算
		
		int N1=0;//停在第一層的話,第一層是最低的,那麼N1則為0
		int N2=Floor[1];//假設第一次停在第一層,求出目標樓層是當前樓層的人數.
		//計算N3的初始值和停在1層對應的nMinFloor值
		int N3=0;
		int nMinFloor=0;
		for(int i=2;i<=N;i++)
		{
			N3+=Floor[i];
			nMinFloor+=Floor[i]*(i-1);
		}
		//然後逐漸往上走,尋找最優目標樓層。
		for(int i=2;i<=N;i++)
		{
			if(N1+N2-N3<0)//即判斷往上走,爬樓梯總數是否會減少。
			{
				N1+=N2;
				N2=Floor[i];
				N3-=Floor[i];
				stopFloor=i;//更新停電梯樓層.
				nMinFloor+=(N1+N2-N3);//更新爬樓梯總數
				
			}
			else
				break;
		}
		System.out.println("最佳停電梯的樓層為:"+stopFloor);
		System.out.println("乘客需要爬樓梯的總數為:"+nMinFloor);
			

	}

}
        以上解法是從第一層往上走,依次更新相應的資料,直到目標值不能再優化時,得到最優值。執行結果如下:
10 6
4 8 3 9 10 5
最佳停電梯的樓層為:5
乘客需要爬樓梯的總數為:15

4.解法三

        其實,還有一種解法,有n個乘客都有自己需要去的樓層,在停止樓層下電梯之後,大家都要去自己的目標樓層,那麼我們假設每位乘客都已經在目的樓層了,那麼我們可以將問題轉換為,所有乘客在哪一層碰頭,所需要爬的樓梯最少。一般這種會面演算法,最佳地點應該選擇在中間數,即將每一位乘客所在位置,放進一個數組並排序取中間數就是最佳會面地點。(注:當乘客數為n,則最佳地點就是有序地點集合中第(n+1)/2個元素代表的地點)。其中這裡的排序可以用桶排序,因為樓層數不會過於龐大,那麼把每一層都當做一個桶,然後把相應的人放進去,桶號即陣列下標,然後逐個取出就完成了排序,複雜度是線性的。
package bianchengzhimei.part1_8;

import java.util.Scanner;

public class ElevatorController3 {
	
	private static int[] calSort(int[] a,int N,int n)
	{
		int[] res=new int[n];
		int k=0;
		for(int i=0;i<=N;i++)
		{
			int temp=a[i];
			while(temp>0)
			{
				res[k++]=a[i];
			}
		}
		return res;
	}
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int N=sc.nextInt();
		int n=sc.nextInt();
		int passenger[]=new int[N+1];
		for(int i=0;i<n;i++)
		{
			passenger[sc.nextInt()]+=1;
		}
		sc.close();
		int[] res=calSort(passenger,N,n);//桶排序
		int stopFloor=res[(n+1)/2-1];
		int nMinFloor=0;
		for(int i=1;i<=N;i++)
		{
			nMinFloor+=(i-stopFloor)*passenger[i];
		}
		System.out.println("最佳停電梯的樓層為:"+stopFloor);
		System.out.println("乘客需要爬樓梯的總數為:"+nMinFloor);

	}

}
執行結果:
10 8
1 1 2 7 8 8 8 9
最佳停電梯的樓層為:7
乘客需要爬樓梯的總數為:22