9.3多階段決策問題

每做一次決策就可以得到解得一部分,當做完所有決策做完之後,完整的解就浮出水面了。在回溯法中,每次決策對應給一個結點產生新的子樹,而解的生成過程對應一顆解答樹,結點的層數就是“下個待填充位置”cur

9.3.1多段圖的最短路

多段圖是一種特殊的DAG,其結點可以劃分成若干個階段,每個階段只有上一個階段所決定

例題:單向TSP

分析:

在這個題目中,每一列就是一個階段,每個階段有三種決策

多階段決策的最優化問題往往可以通過用動態規劃解決,其中,狀態及其轉移類似於回溯法中的解答樹,解答樹的中的“層數”,也就是遞歸函數中的“當前填充位置cur”,描述的是即將完成的決策序號,在動態規劃中被稱爲階段

有了前面的經驗,不難設計出狀態,設d(i,j)爲從格子(i,j)出發到最後一列的最小開銷,但是本題不僅要輸出解,還要求字典序最小,這就需要在計算d(i,j)的同時記錄“下一列的行號”的最小值

int ans=INF,first=0;
for(int j=n-1;j>=0;j--){
    for(int i=0;i<m;i++){
        if(j==n-1)
            d[i][j]=a[i][j];  //邊界
        else{
            int rows[3]={i,i-1,i+1};
            if(i==0) rows[1]=m-1;
            if(i==m-1) rows[2]=0;
            sort(rows,rows+3);  //重新排序,以便找到字典序最小
            d[i][j]=INF;
            for(int k=0;k<3;k++){
                int v=d[rows[k]][j+1]+a[i][j];
                if(v<d[i][j]) {d[i][j]=v;next[i][j]=rows[k]; }
            }
        }
        if(j==0&&d[i][j]<ans)
        {
            ans=d[i][j];
            first=i;
        }
    }
    
}
printf("%d",first+1);  //輸出第一列
for(int i=next[first][0],j=1;j<n;i=next[i][j],j++)
    printf(" %d",i+1);
printf("\n%d\n",ans);

代碼還有一定的問題

9.3.2 0-1揹包問題

下面是代碼,答案是d[1][C];

for(int i=n;i>=1;i--)
for(int j=0;j<=C;j++){
    d[i][j]=(i==n?0:d[i+1][j]);   //d[i][j]表示把第i,i+1,i+2,,n個物品裝到容量爲j的揹包的最大重量
    if(j>=V[i])
        d[i][j]=max(d[i][j],d[i+1][j-V[i]]+W[i]);
}
//答案是d[1][C];
還有另一種“對稱”的狀態定義:用f[i][j]表示“把前i個物品裝到容量爲j的揹包中的最大總重量”,其狀態轉移方程也不難得出

for(int i=1;i<=n;i++)
for(int j=0;j<=C;j++){
    f[i][j]=(i==1?0:f[i-1][j]);
    if(j>=V[i])
        f[i][j]=max(f[i][j],f[i-1][j-V[i]]+W[i]);
}
看上去這兩種方式完全對稱的,但是其存在細微區別,新的方法可以邊讀入邊計算,而不必把V和W保存下來

for(int i=1;i<=n;i++){
    scanf("%d%d",&V,&W);
    for(int j=0;j<=C;j++){
        f[i][j]=(i==1?0:f[i-1][j]);
        if(j>=V)
            f[i][j]=max(f[i][j],f[i-1][j-V]+W);
    }
}






發佈了38 篇原創文章 · 獲贊 2 · 訪問量 8044
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章