目录

算法实验报告

大三上学期的算法课程的实验报告。

问题描述

3-15 收集样本问题

机器人Rob在一个有个方格的方形区域中收集样本。方格中样本的价值为,如图所示。Rob从方形区域的左上角点出发,向下或向右行走,直到右下角的点,在走过的路上,收集方格中的样本。Rob从点到点共走2次,试找出Rob的2条行走路径,使其取得的样本总价值最大。

/assignment-algorithm-report/%E5%9B%BE%E7%89%871.png

算法设计: 给定方形区域中的样本分布,计算Rob的2条行走路径,使其取得的样本总价值最大。

数据输入: 由文件input.txt给出输入数据。第1行有1个正整数,表示方形区域个方格。接下来每行有3个整数,前2个数表示方格位置,第3个数为该位置样本价值。最后一行是3个0。

结果输出: 将计算的最大样本总价值输出到文件output.txt。

/assignment-algorithm-report/%E5%9B%BE%E7%89%872.png

问题求解

最优子结构性质

本题可以使用动态规划算法来求解,因此首先证明具有最优子结构性质。因为每一步只能选择向右或者向下行走,可以按行走的距离对区域内的方格进行划分:距离为0的包括(0,0),距离为1的包括(0,1)和(1,0),距离为2的包括(0,2)(1,1)和(2,0)…以此类推,所有路线都会经过这样以距离为划分的等价类区域一次且只有一次,如图所示。

/assignment-algorithm-report/%E5%9B%BE%E7%89%873.png

以图中的点为例,考察子问题:求到达点收集的最大样本值,机器人只可能经由,而与其他点以及距离更远的点无关。假设有一条路径到达,使得样本值最大,则对于这条路径上的前一个点来说,到达该点时的样本值也一定是相应子问题的最优解,否则用另一条最优解的路径替换当前的路径,得到到达的路径一定会比“最优解”要更大,产生了矛盾。因此该问题具有最优子结构性质。

求解思路

具体到本问题,因为题目要求行走2次得到的结果,可以用一个四维的矩阵来存储计算的中间值(这四个维度分别为两个坐标的横纵坐标值)。按照距离为划分,对所有的方格依次进行遍历,遍历到某一个的方格时,更新它的右方和下方方格的值。

/assignment-algorithm-report/%E5%9B%BE%E7%89%874.png

为了对路径进行记录,建立一个四维的方向矩阵,当方格的值进行更新时,同步记录行走至该方格的方向。方向用一个int值表示,其中的两个位分别用于表示两次行走的方向(BIT1位和BIT2位),当方向为向右时,此时y坐标增加1,BIT位为1;当方向朝下时,此时x坐标增加1,BIT位为0。

注意
我使用了Eigen库的Matrix类型作为数据的存储容器。

算法描述

从文件中读取价值数据,并存入价值矩阵V中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
std::ifstream in_file{"input.txt"};
int n;
in_file >> n;
const int nm = n - 1;
MatrixXi V = MatrixXi::Zero(n, n);
{
    int i, j, v;
    in_file >> i >> j >> v;
    while (i != 0 && j != 0 && v != 0) {
        V(i - 1, j - 1) = v;
        in_file >> i >> j >> v;
    }
}
in_file.close();

初始化缓存价值矩阵EV,以及方向记录矩阵D

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
MatrixXXi EV{n, n}, D{n, n};
for (int x1 = 0; x1 < n; ++x1) {
    for (int y1 = 0; y1 < n; ++y1) {
        EV(x1, y1) = MatrixXi::Zero(n, n);
        D(x1, y1) = MatrixXi::Zero(n, n);
        if (x1 == 0) {
            D(x1, y1).setConstant(BIT1);
        }
        for (int y2 = 0; y2 < n; ++y2) {
            D(x1, y1)(0, y2) |= BIT2;
        }
    }
}

定义更新函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int mv = 0;
auto update = [&V, &EV, &D, &mv](int x1, int y1, int x2, int y2, int v, int d) {
    v += V(x1, y1);
    if (x1 != x2 || y1 != y2) {
        v += V(x2, y2);
    }
    if (v > EV(x1, y1)(x2, y2)) {
        EV(x1, y1)(x2, y2) = v;
        D(x1, y1)(x2, y2) = d;
    }
    if (v > mv) {
        mv = v;
    }
};

遍历整个矩阵

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
auto time_start = high_resolution_clock::now();

EV(0, 0)(0, 0) = V(0, 0);
for (int s = 0; s < nm + nm; ++s) {
    int op = 0, ed = s;
    if (s > nm) {
        op = s - nm, ed = nm;
    }
    for (int x1 = op; x1 <= ed; ++x1) {
        for (int x2 = op; x2 <= ed; ++x2) {
            int y1 = s - x1;
            int y2 = s - x2;
            int v = EV(x1, y1)(x2, y2);
            if (x1 != nm) {
                if (x2 != nm) update(x1 + 1, y1, x2 + 1, y2, v, 0);
                if (y2 != nm) update(x1 + 1, y1, x2, y2 + 1, v, BIT2);
            }
            if (y1 != nm) {
                if (x2 != nm) update(x1, y1 + 1, x2 + 1, y2, v, BIT1);
                if (y2 != nm) update(x1, y1 + 1, x2, y2 + 1, v, BIT1 | BIT2);
            }
        }
    }
}

auto time_stop = high_resolution_clock::now();
// 计算花费时间
auto duration = duration_cast<microseconds>(time_stop - time_start);
cout << "Time cost: " << duration.count() << "ms" << endl;

将结果写入文件中

1
2
3
std::ofstream out_file{"output.txt"};
out_file << EV(nm, nm)(nm, nm);
out_file.close();

还原并输出路线图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
wchar_t wh = L'◽', bl = L'◾';
MatrixXch M1{n, n}, M2{n, n};
M1.setConstant(wh), M2.setConstant(wh);
int x1 = nm, y1 = nm, x2 = nm, y2 = nm;
while (x1 != 0 || y1 != 0) {
    M1(x1, y1) = bl, M2(x2, y2) = bl;
    int d = D(x1, y1)(x2, y2);
    if (d & BIT1) {
        --y1;
    } else {
        --x1;
    }
    if (d & BIT2) {
        --y2;
    } else {
        --x2;
    }
}
M1(x1, y1) = bl, M2(x2, y2) = bl;

wcout << "Path 1:" << endl << M1 <<
      "Path 2:" << endl << M2;

运行结果

运行程序后,output.txt文件中的内容为67。控制台输出如下:

/assignment-algorithm-report/%E5%9B%BE%E7%89%875.png

可以看到,图中的两条路径符合正确的结果。

完整代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <iostream>
#include <fstream>
#include <eigen3/Eigen/Dense>
#include <io.h>
#include <fcntl.h>
#include <chrono>

using std::cout, std::wcout, std::endl;
using std::chrono::high_resolution_clock,
        std::chrono::duration_cast, std::chrono::microseconds;
using Eigen::Matrix, Eigen::MatrixXi, Eigen::Dynamic;
using MatrixXXi = Matrix<MatrixXi, Dynamic, Dynamic>;
using MatrixXch = Matrix<wchar_t, Dynamic, Dynamic>;
const int BIT1 = 1 << 0, BIT2 = 1 << 1;

std::wostream &operator<<(std::wostream &o, const MatrixXch &m) {
    for (int i = 0; i < m.rows(); ++i) {
        for (int j = 0; j < m.cols(); ++j) {
            o << m(i, j);
        }
        o << endl;
    }
    return o;
}

int main() {
    // 从文件中读取价值数据,并存入价值矩阵V中
    std::ifstream in_file{"input.txt"};
    int n;
    in_file >> n;
    const int nm = n - 1;
    MatrixXi V = MatrixXi::Zero(n, n);
    {
        int i, j, v;
        in_file >> i >> j >> v;
        while (i != 0 && j != 0 && v != 0) {
            V(i - 1, j - 1) = v;
            in_file >> i >> j >> v;
        }
    }
    in_file.close();

    // 初始化缓存价值矩阵EV,以及方向记录矩阵D
    MatrixXXi EV{n, n}, D{n, n};
    for (int x1 = 0; x1 < n; ++x1) {
        for (int y1 = 0; y1 < n; ++y1) {
            EV(x1, y1) = MatrixXi::Zero(n, n);
            D(x1, y1) = MatrixXi::Zero(n, n);
            if (x1 == 0) {
                D(x1, y1).setConstant(BIT1);
            }
            for (int y2 = 0; y2 < n; ++y2) {
                D(x1, y1)(0, y2) |= BIT2;
            }
        }
    }

    // 更新函数
    int mv = 0;
    auto update = [&V, &EV, &D, &mv](int x1, int y1, int x2, int y2, int v, int d) {
        v += V(x1, y1);
        if (x1 != x2 || y1 != y2) {
            v += V(x2, y2);
        }
        if (v > EV(x1, y1)(x2, y2)) {
            EV(x1, y1)(x2, y2) = v;
            D(x1, y1)(x2, y2) = d;
        }
        if (v > mv) {
            mv = v;
        }
    };

    // 遍历整个矩阵
    auto time_start = high_resolution_clock::now();

    EV(0, 0)(0, 0) = V(0, 0);
    for (int s = 0; s < nm + nm; ++s) {
        int op = 0, ed = s;
        if (s > nm) {
            op = s - nm, ed = nm;
        }
        for (int x1 = op; x1 <= ed; ++x1) {
            for (int x2 = op; x2 <= ed; ++x2) {
                int y1 = s - x1;
                int y2 = s - x2;
                int v = EV(x1, y1)(x2, y2);
                if (x1 != nm) {
                    if (x2 != nm) update(x1 + 1, y1, x2 + 1, y2, v, 0);
                    if (y2 != nm) update(x1 + 1, y1, x2, y2 + 1, v, BIT2);
                }
                if (y1 != nm) {
                    if (x2 != nm) update(x1, y1 + 1, x2 + 1, y2, v, BIT1);
                    if (y2 != nm) update(x1, y1 + 1, x2, y2 + 1, v, BIT1 | BIT2);
                }
            }
        }
    }

    auto time_stop = high_resolution_clock::now();
    // 计算花费时间
    auto duration = duration_cast<microseconds>(time_stop - time_start);
    cout << "Time cost: " << duration.count() << "ms" << endl;

    // 将结果写入文件中
    std::ofstream out_file{"output.txt"};
    out_file << EV(nm, nm)(nm, nm);
    out_file.close();

    // 还原路线图
    wchar_t wh = L'◽', bl = L'◾';
    MatrixXch M1{n, n}, M2{n, n};
    M1.setConstant(wh), M2.setConstant(wh);
    int x1 = nm, y1 = nm, x2 = nm, y2 = nm;
    while (x1 != 0 || y1 != 0) {
        M1(x1, y1) = bl, M2(x2, y2) = bl;
        int d = D(x1, y1)(x2, y2);
        if (d & BIT1) {
            --y1;
        } else {
            --x1;
        }
        if (d & BIT2) {
            --y2;
        } else {
            --x2;
        }
    }
    M1(x1, y1) = bl, M2(x2, y2) = bl;

    // 配置使C++可以输出Unicode
    constexpr char cp_utf16le[] = ".1200"; // UTF-16 little-endian locale.
    setlocale(LC_ALL, cp_utf16le);
    _setmode(_fileno(stdout), _O_WTEXT);

    // 输出路线图
    wcout << "Path 1:" << endl << M1 <<
          "Path 2:" << endl << M2;
}