演算法作業(裝載問題)
實驗六 裝載問題
###問題描述與實驗目的:
有n個集裝箱要裝上2艘載重量分別為c1和c2的輪船,其中第i個集裝箱的重量為wi,要求確定是否有一個合理的裝載方案可將這個集裝箱裝上這2艘輪船。如果有,找出一種裝載方案。
注意,在滿足的條件下才可能將這個集裝箱裝上這2艘輪船。
###輸入
輸入有若干組測試資料(不超過20組)。
每組測試資料有3行:其第1行上是集裝箱個數n,(n<20),第2行上有n個整數w1、w2、…、wn,整數之間用一個空格分開,這n個整數依次表示這n個集裝箱的重量,接下來的一行上有2個整數c1、c2,表示這兩艘船的載重量,(0<wi <1000,i=1,2,…,n,0<c1,c2<30000)。
###輸出
現要求對輸入中的每組測試資料,輸出2行:
在第1行上輸出“Case #”,其中“#”是測試資料的組號(從1開始)。
在第2行上輸出具體裝載結果:如不能裝載則輸出“No”;否則輸出一個整數bestw及一個由0或1構成的字串x1x2…xn,其中bestw表示第一艘船能裝的最大總載重量,x1x2…xn是對應於bestw的具體裝載方案,xi=1表示第i個集裝箱裝在第一艘船上,而xi=0表示第i個集裝箱不裝在第一艘船上。
對應於bestw的具體裝載方案可能不唯一,如重量分別為10、40、40的3個集裝箱,若兩船的載重量都是50,那麼有裝載方案110和101兩種。為使輸出結果可操作,我們約定長為n的0-1字串以字典序最大的那個為符合要求的裝載方案。
如可能,將計算時間複雜性限制在$ O(2^n)$
###輸入樣例
3
10 40 40
50 50
3
20 40 40
50 50
###輸出
Case 1
50 110
Case 2
No
##分析:
本次實驗的重點在於將
的複雜度優化到$ O(2^n)$,我採用的方法是,在dfs過程中動態的更新路徑best。具體實現是:在dfs時記錄x[i]和best[i],在回溯的過程中,用x[i]來更新best[i]。
dfs結束以後,看記錄的最多可裝載量ans+c2與總量之間的關係,如果ans+c2<tot,那麼之間輸出No,反之,輸出ans以及路徑。
###執行結果:
###原始碼:
#include <bits/stdc++.h> using namespace std; const int maxn=100; int n; int a[maxn]; int c1,c2; int x[maxn],ans,best[maxn]; int cw; int ii,r; void dfs(int i) { if(i>n) { ii=n; ans=cw; return; } r-=a[i]; if(cw+a[i]<=c1) { x[i]=1; cw+=a[i]; dfs(i+1); if(ii==i) { best[i]=1; ii--; } cw-=a[i]; } if(cw+r>ans) { x[i]=0; dfs(i+1); if(ii==i) { best[i]=0; ii--; } } r+=a[i]; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int Case=0; while(scanf("%d",&n)!=EOF) { if(n==0) break; int s=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); s+=a[i]; } cw=0,ans=0; r=s; scanf("%d%d",&c1,&c2); dfs(1); printf("Case %d\n",++Case); if(ans+c2<s) { printf("No\n"); continue; } printf("%d ",ans); for(int i=1;i<=n;i++) { printf("%d",best[i]); } printf("\n"); } return 0; }
實驗體會
通過本次實驗,我對回溯的理解又加深了,也體會到了剪支對搜尋演算法非常有用。