递归自顶向下,动规自底向上。
递归对应搜索,动规对应递推。
递归必须要记忆化搜索,否则会T,由于使用尾递归,在这个情形下和动规效率相差无几。个人更喜欢递归,因为清晰简洁(=______=)
#include
bool tac[22][22];
int m, n, hx, hy,
dx[8] = {2, 1, -1, -2, -2, -1, 1, 2},
dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};
long long mem[21][21];
using namespace std;
inline bool legal(int x, int y)
{
return x >= 0 && x <= n && y >= 0 && y <= m;
}
void init()
{
int i;
cin >> n >> m >> hx >> hy;
for (i = 0; i <= m+1; i++)
tac[n+1][i] = true;
for (i = 0; i <= n; i++)
tac[i][m+1] = true;
for (i = 0; i < 8; i++)
if (legal(hx+dx[i], hy+dy[i]))
tac[hx+dx[i]][hy+dy[i]] = true;
tac[hx][hy] = true;
// for (int i = 0; i <= n+1; i++)
// {
// for (int j = 0; j <= m+1; j++)
// cout << tac[i][j];
// cout << endl;
// }
}
long long sln(int x, int y)
{
if (tac[x][y])
return 0;
if (x == n && y == m)
return 1;
if (mem[x][y])
return mem[x][y];
return mem[x][y] = sln(x+1, y) + sln(x, y+1);
}
int main()
{
init();
cout << sln(0, 0);
}
动规第一种方法是填表(略)。
动规第二种方法,把递推数组滚动起来。这里不简单地使用两个数组滚动。
如果要滚动两个维度的数组,转移方程为:
dp[i&1][j] = dp[(i-1)&1][j]+dp[i&1][j-1]
或dp[i%2][j] = dp[(i-1)%2][j]+dp[i%2][j-1]
(不建议,因为机器算除法效率低)
我们可以直接滚动一维来实现(来自Loner_Knowledge的题解):
#include
bool tac[22][22];
int m, n, hx, hy,
dx[8] = {2, 1, -1, -2, -2, -1, 1, 2},
dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};
long long dp[22];
using namespace std;
inline bool legal(int x, int y)
{
return x >= 0 && x <= n && y >= 0 && y <= m;
}
void init()
{
int i;
cin >> n >> m >> hx >> hy;
for (i = 0; i <= m + 1; i++)
tac[n + 1][i] = true;
for (i = 0; i <= n; i++)
tac[i][m + 1] = true;
for (i = 0; i < 8; i++)
if (legal(hx + dx[i], hy + dy[i]))
tac[hx + dx[i]][hy + dy[i]] = true;
tac[hx][hy] = true;
dp[0] = 1;
}
int main()
{
init();
for (int i = 0, j; i <= n; ++i)
for (dp[0] *=!tac[i][0], j = 1; j <= m; ++j)
(dp[j] += dp[j - 1]) *= !tac[i][j];
cout << dp[m];
return 0;
}