1. 程式人生 > >演算法作業(裝載問題)

演算法作業(裝載問題)

實驗六 裝載問題

###問題描述與實驗目的:
有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 ( n 2 n )

O(n*2^n) 的複雜度優化到$ O(2^n)$,我採用的方法是,在dfs過程中動態的更新路徑best。具體實現是:在dfs時記錄x[i]和best[i],在回溯的過程中,用x[i]來更新best[i]。
dfs結束以後,看記錄的最多可裝載量ans+c2與總量之間的關係,如果ans+c2<tot,那麼之間輸出No,反之,輸出ans以及路徑。
###執行結果:

Alt text

###原始碼:

#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;
}

實驗體會
通過本次實驗,我對回溯的理解又加深了,也體會到了剪支對搜尋演算法非常有用。