棋盘型DP

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;
} 

你可能感兴趣的:(动态规划)