演算法學習——動態規劃之裝載問題
演算法描述
兩艘船各自可裝載重量為c1,c2,n個集裝箱,各自的重量為w[n],設計一個可以裝載的方案,使得兩艘船裝下全部集裝箱
演算法思路
將第一艘船儘量裝滿(第一艘船放的集裝箱的重量之和接近c1),剩餘的集裝箱放入第二艘船,若剩餘的集裝箱重量之和大於第二艘船,則無解
定義一個一維陣列,
a[n]
存放對應的集裝箱的重量定義一個數組,
m[i][j]
表示第一艘船還可裝載的重量j,可取集裝箱編號範圍為i,i+1...n的最大裝載重量值例如 現在有3個集裝箱 重量分別為9,5,3,即
a[1]=9 a[2]=5 a[3]=3
m[1][2]=0
可裝載重量為2,此時上述的三個集裝箱都不能裝入,所以為最大可裝載重量為0m[1][3]=m[1][4]=3
可裝載重量為3或者是4的時候,都是隻能裝入重量為3的那個集裝箱,所以最大可裝載重量為3 `實際上,這裡的
3=a[3]+m[1][2]
,是一個遞推的關係,具體看下面`m[i][j]
分下面兩種情況0<=j<a[n]
(當可裝載重量j
小於第n個集裝箱的重量w[n]
,此時就不能往船上裝此集裝箱)m[i][j] = m[i+1][j]
j>=a[n]
(可裝載重量j
大於或等於第n個集裝箱的重量w[n]
),此時剩餘的可裝載重量為j-a[n]
(裝入了此時的集裝箱),最大的可裝載重量為m[i+1][j-w[n]]+w[n]
但是我們是需要最大的可裝載重量,所以得與如果不將當前集裝箱裝入的那種情況
m[i+1][j]
進行比較m[i][j]=Math.max(m[i+1][j],m[i+1][j-a[n]+a[n]])
上面我們就獲得了一個關於
m[i][j]
的遞推關係,我們通過逆推獲得全部的數值初始值
m[i][j]=0
這裡的i=n
j從0到a[n]
這裡的a[n]是第n個集裝箱重量(最後一個集裝箱的重量)這裡的賦值其實就是上述m[i][j]兩種情況的第一種情況,最後一個集裝箱的重量大於可裝載重量,不裝載此集裝箱,所以最大可裝載重量為0,
m[i][j]=a[n]
這裡的i=n
j從a[n]到c1
這裡的意思為當可裝載重量j只要都是大於最後一個集裝箱的重量a[n],即可裝入此集裝箱,所以最大可裝載重量等於裝入的集裝箱的重量
開始逆推
使用上述的遞推公式進行逆推
for (int i = n; i >= 1 ; i--) { for (int j = 1; j <=c1; j++) { if(j>=a[i]){ m[i][j] = Math.max(m[i+1][j],m[i+1][j-a[i]]+a[i]); }else{ m[i][j]=m[i+1][j]; } } }
之後再進行輸出,輸出第一艘船的裝載方案,輸出第二艘船的裝載方案
演算法實現
System.out.println("輸入第一艘船可裝載重量c1:");
Scanner scanner = new Scanner(System.in);
int c1 = scanner.nextInt();
System.out.println("輸入第二艘船可裝載重量c2:");
int c2 = scanner.nextInt();
System.out.println("輸入集裝箱個數n:");
int n = scanner.nextInt();
int[] a = new int[n+1];
//使用一維陣列存放集裝箱重量
System.out.println("依次輸入集裝箱的重量");
for (int i =1; i < n+1; i++) {
a[i] = scanner.nextInt();
}
int sum = 0;//集裝箱重量總和
for (int i = 0; i < a.length; i++) {
sum=sum+a[i];
}
//超重情況
if(sum>c1+c2){
System.out.println("集裝箱重量之和大於兩艘船可裝載重量,題目無解");
return;//結束程式
}
int[][] m = new int[100][100];//m[i][j]表示第一艘船還可裝載的重量j,可取集裝箱編號範圍為i,i+1...n的最大裝載重量值
//賦初始值,由於是逆推,所以從末尾開始
//可裝載重量j小於第n個集裝箱重量a[n],不裝此集裝箱,賦值為0
for (int j = 0; j < a[n]; j++) {
m[n][j] = 0;
}
//可裝載重量j大於或等於第n個集裝箱重量a[n],裝載此集裝箱,此時刻最大裝載重量值為a[n]
for (int j = a[n]; j <=c1 ; j++) {
m[n][j]=a[n];
}
//關鍵逆推程式碼
for (int i = n; i >= 1 ; i--) {
for (int j = 1; j <=c1; j++) {
if(j>=a[i]){
m[i][j] = Math.max(m[i+1][j],m[i+1][j-a[i]]+a[i]);
}else{
m[i][j]=m[i+1][j];
}
}
}
int maxc1 = m[1][c1];//最大可裝載重量
System.out.println("maxc1="+maxc1);
if(maxc1>sum-c2){
int cw = m[1][maxc1];
int sw,i;
//輸出第一艘船的裝載
System.out.println("第一艘船裝載:");
for (sw=0,i=1;i<=n;i++){
if(m[i][cw]>m[i+1][cw]){
cw = cw-a[i];
sw=sw+a[i];//統計sw,sw的最終結果與maxc1相等
System.out.print(a[i]+" ");
a[i]=0;//裝載當前的集裝箱
}
}
System.out.print("("+sw+")");
System.out.println("");
//輸出第二艘船的裝載
System.out.println("第二艘船裝載:");
for(sw=0,i=1;i<=n;i++){
//已裝載在第一艘船的集裝箱a[i]都已經為0了,只需要將不為0的那些集裝箱裝入第二艘船即可
if(a[i]!=0){
System.out.print(a[i]+" ");
sw=a[i]+sw;
}
}
System.out.println("("+sw+")");
}else{
System.out.println("無解");
}