程式設計之美之小飛的電梯排程演算法(多種解法)---Java語言
阿新 • • 發佈:2018-12-26
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