棋盘型DP

来源:互联网 发布:在家网络兼职的工作 编辑:程序博客网 时间:2024/05/16 18:37

1.过河卒(NOIP2002普及组)
http://codevs.cn/problem/1010/

算法:状态转移就是向下和向右两个方向,方程很好写。

PS:用记忆化搜索更方便一些。

代码:

#include<bits/stdc++.h>#define MAX_N 20using 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<bits/stdc++.h>#define MAX_N 51using 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<bits/stdc++.h>#define MAX_N 51using 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<bits/stdc++.h>#define MAX_N 101using 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;} 
0 0