Блог пользователя Qumeric

Автор Qumeric, 9 лет назад, По-русски

Иногда встречаются задачи с дву/многомерными массивами, в которых есть несколько очевидных случаев возможного выхода за пределы массива. Они просты и понятны, но для их обработки порой требуется много "лишнего" кода (однажды автор написал 16 if-ов!). Я придумал простой способ, который поможет избежать этого. Возможно, это покажется вам очевидным, но лично мне так не казалось еще вчера. Думаю, я не один такой.

Пример задачи (как можно более простой, взята отсюда http://informatics.mccme.ru/mod/statements/view3.php?chapterid=946 ):

Дана прямоугольная доска N × N (N <= 20). Конь стоит в верхнем левом углу доски. Выведите количество способов добраться конём до правого нижнего угла доски, если конь может ходить только так:

Вот обычное решение этой задачи динамическим программированием:

#include <iostream>

int n, dp[20][20];

int main() {
    std::cin >> n;
    dp[0][0] = 1;
    for (int i = 1; i < n; i++) {
        for (int j = 1; j < n; j++) {
            if (i-2 >= 0 && j-1 >= 0)
                dp[i][j] += dp[i-2][j-1];
            if (i-1 >= 0 && j-2 >= 0)
                dp[i][j] += dp[i-1][j-2];
        }
    }
    std::cout << dp[n-1][n-1] << '\n';
}

Казалось бы, ничего сложного. Но тем не менее его можно упростить:

#include <iostream>

int n, dp[20][20];

int f(int i, int j) {
    return (i < 0 || j < 0) ? 0 : dp[i][j];
}

int main() {
    std::cin >> n;
    dp[0][0] = 1;
    for (int i = 1; i < n; i++)
        for (int j = 1; j < n; j++)
            dp[i][j] = f(i-2, j-1) + f(i-1, j-2);
    std::cout << dp[n-1][n-1] << '\n';
}

В данном примере не видно большой разницы, но в некоторых случаях это может действительно сократить код, тем самым уменшив возможность ошибки. Это способ хорош еще тем, что если вдруг надо будет возвращать не 0 в случае выхода за пределы, а, например -inf, то это легко сделать.

Кстати, функцию f можно сделать более гибкой, передав ей массив/вектор в качестве аргумента, например: int f(vector<int>& v, int i, int j) {, а можно даже использовать C++14 и написать так: int f(auto& v, int i, int j) {.

  • Проголосовать: нравится
  • +5
  • Проголосовать: не нравится

»
9 лет назад, # |
  Проголосовать: нравится +2 Проголосовать: не нравится

Твой код не обрабатывает случай когда индекс больше либо равен размеру массива

Обычно я делаю так

inline bool ok(int i, int j, int n, int m){return 0<=i&&i<n&&0<=j&&j<m;}
inline bool ok(int i, int n){return 0<=i&&i<n;}

int main() {
    std::cin >> n;
    dp[0][0] = 1;
    for (int i = 1; i < n; i++) {
        for (int j = 1; j < n; j++) {
            if (ok(i-2, j-1, n, n))
                dp[i][j] += dp[i-2][j-1];
            if (ok(i-1, j-2, n, n))
                dp[i][j] += dp[i-1][j-2];
        }
    }
    std::cout << dp[n-1][n-1] << '\n';
}
»
9 лет назад, # |
Rev. 2   Проголосовать: нравится +11 Проголосовать: не нравится

Я бы так делал:

#include <iostream>

int n, dp[21][21];

int main() {
    std::cin >> n;
    memset(dp, 0, sizeof(dp));
    dp[1][1] = 1;
    for (int i = 2; i < n + 1; i++) 
        for (int j = 2; j < n + 1; j++) 
            dp[i][j] += dp[i-1][j-2] + dp[i-2][j-1];

    std::cout << dp[n][n] << '\n';
}