1.过河卒(NOIP2002普及组)
http://codevs.cn/problem/1010/
算法:状态转移就是向下和向右两个方向,方程很好写。
PS:用记忆化搜索更方便一些。
代码:
#include
#define MAX_N 20
using namespace std;
long long f[MAX_N][MAX_N];
int n, m, X, Y;
long long search(int x, int y)
{
if ((x > n) || (y > m)) return 0;
if ((abs(x-X) == 1) && (abs(y-Y) == 2)) return 0;
if ((abs(x-X) == 2) && (abs(y-Y) == 1)) return 0;
if ((x == X) && (y == Y)) return 0;
if (f[x][y] != 0) return f[x][y];
f[x][y] = search(x+1, y) + search(x, y+1);
return f[x][y];
}
int main()
{
cin >> n >> m >> X >> Y;
memset(f, 0, sizeof(f));
f[n][m] = 1;
cout << search(0, 0);
return 0;
}
2.骑士游历(NOIP1997)
http://codevs.cn/problem/1219/
算法:状态转移四个方向,用常量数组dx和dy实现x和y坐标的变化。
PS:还是写成记忆化更方便一些。
代码:
#include
#define MAX_N 51
using namespace std;
const int dx[4] = {1, 1, 2, 2};
const int dy[4] = {2, -2, 1, -1};
int n, m;
int x1, yy1, x2, y2;
long long f[MAX_N][MAX_N];
long long search(int x, int y)
{
if (x > x2) return 0;
if ((y < 1) || (y > m)) return 0;
if (f[x][y] != 0) return f[x][y];
for (int k = 0; k < 4; k++)
f[x][y] += search(x+dx[k], y+dy[k]);
return f[x][y];
}
int main()
{
cin >> n >> m;
cin >> x1 >> yy1 >> x2 >> y2;
memset(f, 0, sizeof(f));
f[x2][y2] = 1;
cout << search(x1, yy1);
return 0;
}
3.传纸条(NOIP2008提高组)
http://codevs.cn/problem/1169/
方程:
f[i][j][k][l]=max{f[i-1][j][k-1][l], f[i-1][j][k][l-1], f[i][j+1][k-1][l], f[i][j+1][k][l-1]}+h[i][j]+h[k][l]
首先我们认为第二个纸条也是从(1,1)出发。
f[i][j][k][l]同时描述两个纸条的传递,含义是第一个纸条到(i,j)位置,第二个纸条到(k,l)位置的最优解。
算法:
这是一个多线程的动态规划。不难发现i,j,k,l之间有这样的关系i+j=k+l,这样就可以把四维降成三维,减少时空复杂度。
代码:
#include
#define MAX_N 51
using namespace std;
int n, m;
int h[MAX_N][MAX_N];
int f[MAX_N][MAX_N][MAX_N];
int search(int x1, int y1, int x2, int y2)
{
if ((x1 == n) && (y1 == m) && (x2 == n) && (y2 == m)) return 0;
if ((x1 > n) || (y1 > m) || (x2 > n) || (y2 > m)) return 0;
if ((x1 == x2) && (y1 == y2)) return 0;
if (f[x1][y1][x2] != 0) return f[x1][y1][x2];
f[x1][y1][x2] = max(f[x1][y1][x2], search(x1+1, y1, x2+1, y2));
f[x1][y1][x2] = max(f[x1][y1][x2], search(x1+1, y1, x2, y2+1));
f[x1][y1][x2] = max(f[x1][y1][x2], search(x1, y1+1, x2+1, y2));
f[x1][y1][x2] = max(f[x1][y1][x2], search(x1, y1+1, x2, y2+1));
f[x1][y1][x2] += h[x1][y1] + h[x2][y2];
return f[x1][y1][x2];
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> h[i][j];
memset(f, 0, sizeof(f));
cout << search(1, 2, 2, 1);
return 0;
}
4.数字三角形
http://codevs.cn/problem/1220/
方程:
f[i][j] = max{[i-1][j], f[i-1][j-1]}+a[i][j]
PS:很经典的递推。
代码:
#include
#define MAX_N 101
using namespace std;
int n;
int a[MAX_N][MAX_N], f[MAX_N][MAX_N];
int main()
{
cin >> n;
memset(a, 0 ,sizeof(a));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
cin >> a[i][j];
f[0][1] = -1;
f[0][0] = -1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
{
f[i][j] = max(f[i][j], f[i-1][j]);
f[i][j] = max(f[i][j], f[i-1][j-1]);
f[i][j] += a[i][j];
}
int res = f[n][1];
for (int i = 1; i <= n; i++)
res = max(res, f[n][i]);
cout << res;
return 0;
}