/*
* Problem :: 1520 / 내리막 길
*
* Kind :: Dynamic Programming + Graph
*
* Insight
* - BFS 로 풀면 되겠다
* + 시간초과네
* # 경로를 일일이 세니까 문제다
* 그러고보니 최대 경로의 수가 10억이네 ㄷㄷ
* -> BFS 로는 못 풀겠다
*
* - 아무래도 DP 를 활용해서 경로의 수를 저장해야 할듯 싶다
* + dp[i][j] = (1,1) -> (i,j) 이동 가능한 경로의 수
* # dp[i][j] 는 상하좌우 인접한 칸들(ai,aj) 중
* 현재 칸보다 높이가 더 높은 칸들의 dp[ai][aj] 의 합으로 구할 수 있다!
* -> 현재 칸보다 높이가 더 높은 인접한 칸들이 없다면 dp[i][j] = 0 이다
* => 제일 왼쪽 위 칸에서 시작해야되니 dp[0][0] = 1 로 초기화 해야한다
*
* Point
* - 편의상 제일 왼쪽 위 칸을 (1,1) 이 아니라 (0,0) 으로 설정하였다
*/
//
// BOJ
// ver.C++
//
// Created by GGlifer
//
// Open Source
#include <iostream>
#include <cstring>
using namespace std;
#define endl '\n'
// Set up : Global Variables
int M, N;
int A[500][500];
int dp[500][500];
int dy[4] = {-1, 0, 0, +1};
int dx[4] = {0, -1, +1, 0};
// Set up : Functions Declaration
bool isValid(int y, int x);
int f(int cy, int cx);
int main()
{
// Set up : I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Set up : Input
cin >> M >> N;
for (int i=0; i<M; i++)
for (int j=0; j<N; j++)
cin >> A[i][j];
// Process
/* dp[i][j] = (0,0) -> (i,j) 내리막길 경로의 개수 */
memset(dp, -1, sizeof(dp));
dp[0][0] = 1; /* 제일 왼쪽 위 칸 시작점(0,0)의 dp 초기화 */
int ans = f(M-1, N-1); /* (0,0) -> (M-1,N-1) 내리막길 경로의 개수 */
// Control : Output
cout << ans << endl;
}
// Helper Functions
bool isValid(int y, int x)
/* 주어진 좌표(y,x)가 유효하면 true를 반환 그 외 false 를 반환 */
{
return y >= 0 && y < M && x >= 0 && x < N;
}
int f(int cy, int cx)
/* dp[i][j] 에 (0,0) -> (i,j) 내리막길 경로의 개수를 저장 및 반환 */
{
if (dp[cy][cx] != -1) return dp[cy][cx];
dp[cy][cx] = 0;
for (int i=0; i<4; i++) {
int ay = cy + dy[i];
int ax = cx + dx[i];
/* 인접한 칸들의 높이가 현재 칸들의 높이보다 높아야만
* 인접한 칸으로 부터 현재 칸으로 오는 내리막 길이 존재할 수 있음 */
if (isValid(ay, ax) && A[ay][ax] > A[cy][cx]) {
dp[cy][cx] += f(ay, ax);
}
} return dp[cy][cx];
}