我们在解决动态规划问题的时候,往往不会很轻松的写出递推方程。这时候我们需要考虑一下是否需要借鉴"多阶段决策问题"。

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

上面的这道“硬币问题”的题目,如果将每一枚硬币的数目改成1,那原先的递推方程就很难写出了,我们不知道何时拿硬币,拿哪一枚硬币。

如果我们将每一枚硬币的标号,视作一个“阶段”,每一个阶段,单独对一个硬币进行分析,就只有两种情况(拿 与 不拿),再循环访问其他硬币,问题就很好解决。

下面给出两个例子,希望可以对比之前较单一的dp问题。

 

单向TSP

问题描述:

  给一个m行n列( m<=10 , n <= 100) 的整数矩阵,从第一列的任何位置出发,每次可以向右,右上,右下走一格,最终到达最后一列的任一行。要求经过的整数之和最小。

注意:

  整个矩阵是环形的,第一行的上一行是最后一行,最后一行的上一行是第一行。多解时,请输出字典序最小的解。

题目分析:

  如果我们设d [ i ] 为第 i 列到最后一列的最小和,我们按照以往的经验写出递推方程 

  \[d[i][j] = \mathop {\max }\limits_j (a[i][j]) + d[i + j])\]

  显然这个方程是错误的,我们不确定 min{ a[ i ][ j ] } 是否能达到 d [ i + 1 ] 的那一个 a [ i + 1] [ j ]。这个时候,我们要考虑将数组 d 扩大为二维。

  这时候,d [ i ][ j ] 设为 从 格子 a[ i ][ j ] 出发到达最后一列的最小整数和。这个时候我们再尝试一下写递推方程,

  \[d[i][j] = \mathop {\max }\limits_{0 <  = k <  = 2} (a[i][j]) + d[nex{\rm{t}}\_rows[k]][j + 1])\]

#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

相关文章:

  • 2021-04-21
  • 2021-12-09
  • 2021-06-13
  • 2021-08-25
  • 2022-03-02
猜你喜欢
  • 2021-10-20
  • 2022-12-23
  • 2022-12-23
  • 2021-10-28
  • 2021-08-19
  • 2021-09-02
  • 2021-06-24
相关资源
相似解决方案