USACO Dynamic Programming (1)

首先看一下题目:

Introduction

Dynamic programming is a confusing name for a programming technique that dramatically reduces the runtime of algorithms: from exponential to polynomial. The basic idea is to try to avoid solving the same problem or subproblem twice. Here is a problem to demonstrate its power:

Given a sequence of as many as 10,000 integers (0 < integer < 100,000), what is the maximum decreasing subsequence? Note that the subsequence does not have to be consecutive.

原题说,有10000个整数,求其中最长递减子序列的长度,其中子序列是可以不连续的。例如,给定序列100, 5, 99, 98,其中最长递减子序列就是100,99,98。从这个例子可以看出,我们很有可能需要舍掉一些小到过分的元素,来保证后面的数字可以大一些,使得更后面的数字有下降的空间。

为了测试的方便,我们将这里的10000个整数暂时换成别的数目的整数。

以下是官方给出的最朴素的解法,我在上做了一些微小的修改。这种做法的实质就是把所有的递减序列找出来,然后找出长度最长的。

#include <iostream>
#include <cstdio>

using namespace std;
const int maxn = 10000;
int n;
int sequence[maxn];

int check(int start, int nmatches, int smallest);

int main() {
    freopen("test.in", "r", stdin);
    cin >> n;
    for(int i = 0; i < n; i++) {
        cin >> sequence[i];
    }
    cout << check(0, 0, 99999);
    return 0;
}

int check(int start, int nmatches, int smallest) {
    cout << "Check!" << endl;
    cout << start << "   " << nmatches << "     " << smallest << endl;
    int better;
    int best = nmatches;
    for(int i = start; i < n; i++) {
        if(sequence[i] < smallest) {
            better = check(i, nmatches + 1, sequence[i]);
            if(better > best) {
                best = better;
            }
        } 
    }
    return best;
}

其中,text.in的数据是我随机生成的,如下:

10
64 65 97 43 5 36 92 72 87 44 

这里的check函数是使用了递归的,递归的终止条件是for循环运行结束,递归的状态转移是在添加了新的数字到递减序列后能达到的最大长度。

如果对于算法一下不能看懂的话,那么可以对照着运行结果看。

运行结果如下:

Check!
0   0     99999
Check!
0   1     64
Check!
3   2     43
Check!
4   3     5
Check!
5   3     36
Check!
4   2     5
Check!
5   2     36
Check!
9   2     44
Check!
1   1     65
Check!
3   2     43
Check!
4   3     5
Check!
5   3     36
Check!
4   2     5
Check!
5   2     36
Check!
9   2     44
Check!
2   1     97
Check!
3   2     43
Check!
4   3     5
Check!
5   3     36
Check!
4   2     5
Check!
5   2     36
Check!
6   2     92
Check!
7   3     72
Check!
9   4     44
Check!
8   3     87
Check!
9   4     44
Check!
9   3     44
Check!
7   2     72
Check!
9   3     44
Check!
8   2     87
Check!
9   3     44
Check!
9   2     44
Check!
3   1     43
Check!
4   2     5
Check!
5   2     36
Check!
4   1     5
Check!
5   1     36
Check!
6   1     92
Check!
7   2     72
Check!
9   3     44
Check!
8   2     87
Check!
9   3     44
Check!
9   2     44
Check!
7   1     72
Check!
9   2     44
Check!
8   1     87
Check!
9   2     44
Check!
9   1     44
4

这里是说,对于递减子序列,我们需要把每一个在原序列中的可能成为当前子序列的末尾元素的元素进行测试。就比如最小子序列的第0号元素,因为任何一个元素都可以成为一个长度为1的递减子序列的,所以最小子序列的第0号元素可能是64,65,97,43,5,36,92,72,87,44中的任何一个。但是假设我们已经选定了第0号元素是64,那么第1号元素就有可能是43,5,36,44中的任意一个。

64 65 97 43 5 36 92 72 87 44
原文地址:https://www.cnblogs.com/NJUWZ/p/7097547.html