1. 程式人生 > >多階段決策問題

多階段決策問題

tps -s fir .cn display stdio.h return ans size

我們在解決動態規劃問題的時候,往往不會很輕松的寫出遞推方程。這時候我們需要考慮一下是否需要借鑒"多階段決策問題"。

https://www.cnblogs.com/woxiaosade/p/10346052.html

上面的這道“硬幣問題”的題目,如果將每一枚硬幣的數目改成1,那原先的遞推方程就很難寫出了,我們不知道何時拿硬幣,拿哪一枚硬幣。

如果我們將每一枚硬幣的標號,視作一個“階段”,每一個階段,單獨對一個硬幣進行分析,就只有兩種情況(拿 與 不拿),再循環訪問其他硬幣,問題就很好解決。

下面給出兩個例子,希望可以對比之前較單一的dp問題。

單向TSP

問題描述:

  給一個m行n列( m<=10 , n <= 100) 的整數矩陣,從第一列的任何位置出發,每次可以向右,右上,右下走一格,最終到達最後一列的任一行。要求經過的整數之和最小。

註意:

  整個矩陣是環形的,第一行的上一行是最後一行,最後一行的上一行是第一行。多解時,請輸出字典序最小的解。

題目分析:

  如果我們設d [ i ] 為第 i 列到最後一列的最小和,我們按照以往的經驗寫出遞推方程

  技術分享圖片

  顯然這個方程是錯誤的,我們不確定 min{ a[ i ][ j ] } 是否能達到 d [ i + 1 ] 的那一個 a [ i + 1] [ j ]。這個時候,我們要考慮將數組 d 擴大為二維。

  這時候,d [ i ][ j ] 設為 從 格子 a[ i ][ j ] 出發到達最後一列的最小整數和。這個時候我們再嘗試一下寫遞推方程,

  技術分享圖片

  

技術分享圖片
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int m, n;
int a[15][105];
int next[15][105];
int d[15][105];
int first = 0, ans = inf ; 
int dp2(int i, int j){//遞歸 if(j == n){ d[i][j] = a[i][j]; return a[i][j]; } if(d[i][j]) return d[i][j]; d[i][j] = inf; int rows[3] = {i, i - 1, i + 1};//行號 直行 右上 右下 if(i == 1) rows[1] = m; if(i == m) rows[2] = 1; for(int k = 0; k < 3; k++){ if(dp2(rows[k], j+1) + a[i][j] < d[i][j]){ d[i][j] = dp2(rows[k], j+1) + a[i][j]; next[i][j] = rows[k]; } } if(j == 0 && d[i][j] < ans){ ans = d[i][j]; first = i; } return d[i][j]; } void dp1(){//循環 for(int i=n;i>0;i--){//逆推列 for(int j=1;j<=m;j++){// if(i==n) d[j][i] = a[j][i]; else{ d[j][i] = inf; int rows[3] = {j, j - 1, j + 1};//行號 直行 右上 右下 if(j == 1) rows[1] = m; if(j == m) rows[2] = 1; sort(rows,rows+3);//保證行號字典序最小 for(int k = 0; k<3;k++){ if(d[rows[k]][i+1] + a[j][i] < d[j][i]){ d[j][i] = d[rows[k]][i+1] + a[j][i]; next[j][i] = rows[k]; } } } if(j == 0 && d[j][i] < ans){ ans = d[j][i]; first = j; } } } } int main(){ scanf("%d %d", &m, &n); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) scanf("%d", &a[i][j]); for(int i=1;i<=m;i++) dp2(i,1); //dp2(1,5); for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++) cout<<d[i][j]<<" "; cout<<endl; } return 0; }
View Code

  如果要輸出路徑,最好設一個next[ i ][ j ] 數組,來記錄 格子( i , j )下一行是哪一行。

0 - 1 背包問題

問題描述:

  有n種物品,每種只有一個,第 i 種 物品的體積為Vi ,重量為 Wi。選一些物品裝到一個容量為C的背包,使得總體積不超C的情況下,重量盡量大。

問題分析:

  模仿上題,將每一個物品看作一個“階段” ,設d [ i ][ j ] 為第 i 到第 n 個物品,裝入到容量為 j 的背包中的最大重量。

  遞推公式: d[ i ][ j ] = max ( d( i + 1 , j) , d( i + 1 , j - V[ i ] ) + W[ i ]) (此物品選擇 不裝 與 裝)

附部分代碼(遞推略)

for(int i = n; i >= 1; i--){
    for(int j = 0; j <= c; j++){
        d[i][j] = (i == n ? 0 : d[i+1][j]);
        if(j >= V[i])
            d[i][j] = max(d[i][j], d[i+1][j - V[i]] + W[i]);
    }
}

參考文獻:

《算法競賽入門經典 (第2版)》劉佳汝 P270

多階段決策問題