「算法提高」动态规划-数字三角形模型

2019-09-15 笔记

Posted by koko on September 15, 2019

[TOC]

动态规划-数字三角形模型

不同题目的逻辑关系:

image-20190914224955707

摘花生

从集合角度来考虑DP问题–闫氏思考法

算法里的坐标画法:

image-20190914230211073

image-20190914230359859

f(2, 3)表示的是所有从(1, 1)走到(2, 3)的路线,存在属性是max。

image-20190914231347555

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;

int m;
int w[N][N], f[N][N];

int main()
{
    cin >> m;
    while (m --)
    {
        int x, y;
        cin >> x >> y;
        for (int i = 1; i <= x; i ++)
            for (int j = 1; j <= y; j ++)
                scanf("%d", &w[i][j]);
                
        for (int i = 1; i <= x; i ++)
            for (int j = 1; j <= y; j ++)
                f[i][j] = max(f[i - 1][j], f[i][j - 1]) + w[i][j];
                
        cout << f[x][y] << endl;
    }
}

最低通行费

N * N 的网格, 限制在 2N - 1的时间,在规定时间内,至少需要的费用。(从左上角走到右下角)

从左上角走到右上角,时间不超过2N - 1意味着什么?

不走回头路的情况下就需要2N - 1的时间,所以意味着不能走回头路

// 因为是求min,要考虑一些边界问题和f[i][j]的状态转换问题。
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110, INF = 1e9;

int m;
int w[N][N], f[N][N];


int main()
{
    int x;
    cin >> x;

    // 一个是从上面走下来,一个是从左边走过来。不能从外部走进来。因为外部走进来是0肯定是更小的。
    for (int i = 1; i <= x; i ++)
        for (int j = 1; j <= x; j ++)
        {
        // 如果是第一行第一列,那就是本身 特判左上角
            scanf("%d", &w[i][j]);
            if (i == 1 && j == 1) f[i][j] = w[i][j];
            else
            {
              // 因为求min还是要把f[i][j]置成极大值才能置换
                f[i][j] = INF; 
              // 只有不在第一行的时候,才能从上面过来
                if (i > 1) f[i][j] = min(f[i][j], f[i - 1][j] + w[i][j]);
              // 只有不在第一列的时候,才能从左边过来
                if (j > 1) f[i][j] = min(f[i][j], f[i][j - 1] + w[i][j]);
            }
        }
            
    cout << f[x][x] << endl;
}

方格取数

从左上角走到右下角,走过的数字置零,并且把数字收集。一共走两次。找出两条这样的路径,让和最大。

image-20190915103057195

走两次:

f[i1, j1, i2, j2] 表示从所有(1, 1),(1, 1)分别走到(i1, j1),(i2, j2)的路径的最大值。

如何处理“同一个格子不能被重复选择”?(同时走的思路)

image-20190915104331191

只有在i1 + j1 == i2 + j2 时, 两条路径的格子才可能重合。

f[k, i1, i2] 表示从所有(1, 1),(1, 1)分别走到(i1, k - i1),(i2, k - i2)的路径的最大值。

注:k == i1 + j1 == i2 + j2; k 表示的是两条路线走到格子的横纵坐标之和。

集合的状态:

image-20190915104224271

两条路线从上往下走的最大值:

image-20190915104158921

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 15;

int n;

int w[N][N], f[N * 2][N][N];

int main()
{
    cin >> n;
    int a, b, c;
    while (cin >> a >> b >> c, a || b || c) w[a][b] = c;
    
    for (int k = 2; k <= 2 * n; k ++)
        for (int i1 = 1; i1 <= n; i1 ++)
            for (int i2 = 1; i2 <= n; i2 ++)
            {
                int j1 = k - i1, j2 = k - i2;
                if (j1 >= 1 && j1 <= n && j2 >= 1 && j2 <= n)
                {
                    int t = w[i1][j1];
                    if (i1 != i2) t += w[i2][j2];
                    
                    int &x = f[k][i1][i2];
                    
                    x = max(x, f[k - 1][i1 - 1][i2 - 1] + t);
                    x = max(x, f[k - 1][i1][i2 - 1] + t);
                    x = max(x, f[k - 1][i1 - 1][i2] + t);
                    x = max(x, f[k - 1][i1][i2] + t);
                }
            }
    
    cout << f[n + n][n][n] << endl;
}

附录:

屈婉玲 离散数学 三部曲

image-20190915105109481

摘花生问题的终极版,K取方格数。走k次,如果用方格取数来做,每多一次就多一维。10条路线,就要考虑1000种情况,所以用最小费用流来做。

本质来说,动态规划问题是图论里的一部分。