题目训练网址(密码hpuacm): https://vjudge.net/contest/245538

其实我之前的博文已经提到过一次记忆化搜索关于01背包的: 文章链接

对于记忆化搜索其实很好理解。就是每次DFS得到的结果用一个用来记忆值的数组保存。直接来看一道题目更好理解。

                                                滑雪

Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子

 1  2  3  4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。

Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。

Output
输出最长区域的长度。

Sample Input
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

Sample Output
25

其实这道题还是有一点不好想想明白的,先看代码。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100 + 3;

int n, m;
int arry[MAXN][MAXN];
int dp[MAXN][MAXN];     // dp记录的是当前从当前点开始能滑的最远的距离
int d[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1, };   // 方向数组,上下左右四个方向
int DFS( int x, int y )
{
    if( dp[x][y] > 0 )      // 之前搜索的结果可以重复利用,不用在搜索一遍了
        return dp[x][y];
    if( dp[x][y] == 0 )     // 走到这一点的时候已经走了一步
        dp[x][y] = 1;
    for( int i=0; i<4; i++ )
    {
        int dx = x + d[i][0];
        int dy = y + d[i][1];
        if( dx >= 0 && dx < n && dy >=0 && dy < m && arry[dx][dy] < arry[x][y] )
        {
            dp[x][y] = max(dp[x][y], DFS(dx, dy) + 1); // 当前的值大还是下一步符合条件的点的值大
        }
    }
    return dp[x][y];
}

int main()
{
    while( scanf("%d%d", &n, &m) != EOF )
    {
        memset(dp, 0, sizeof(dp));
        for( int i=0; i<n; i++ )
        {
            for( int j= 0; j<m; j++ )
                scanf("%d", &arry[i][j]);
        }
        int ans = 0;
        for( int i=0; i<n; i++ )
        {
            for( int j=0; j<m; j++ )
            {
                int temp = DFS(i, j);  // 将矩阵遍历一遍
                ans = max(ans, temp);  // 每次更新最大值
            }
        }
        printf("%d\n", ans);
    }

    return 0;
}

这里提醒一点就是,因为要找到最长的距离,因此我们需要将整个图给便利一边,来找到最合适的起点。那么在此之前搜索的其他点能到达的最远距离还有用吗? 其实是有的,因为我们之前搜索的其他的点得到的距离,其实是那个点与其他点相连的最远距离,没错,我们从其他点出发,当再搜索到这个点的距离时就可以直接加上这个点的最远距离,这样得到的就是,我们从搜索起点出发,所能到达的最远的距离,就是一个叠加总用。这样我们搜索的时候就不用每次将dp数组清零了,很大程度上优化了算法。

 

树形dp我也是刚刚接触,分享一个入门的题,其实很好理解,就是选与不选的问题,有点类似01背包,只不过是在树上的dp

                                            Anniversary party

Description

There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure of employees. It means that the supervisor relation forms a tree rooted at the rector V. E. Tretyakov. In order to make the party funny for every one, the rector does not want both an employee and his or her immediate supervisor to be present. The personnel office has evaluated conviviality of each employee, so everyone has some number (rating) attached to him or her. Your task is to make a list of guests with the maximal possible sum of guests' conviviality ratings.

Input

Employees are numbered from 1 to N. A first line of input contains a number N. 1 <= N <= 6 000. Each of the subsequent N lines contains the conviviality rating of the corresponding employee. Conviviality rating is an integer number in a range from -128 to 127. After that go N – 1 lines that describe a supervisor relation tree. Each line of the tree specification has the form:
L K 
It means that the K-th employee is an immediate supervisor of the L-th employee. Input is ended with the line
0 0 

Output

Output should contain the maximal sum of guests' ratings.

Sample Input

7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0

Sample Output

5

某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上司,现在已知每个人的活跃指数和上司关系,求邀请哪些人来能使得晚会的总活跃指数最大。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 6000+7;

int n;
int dp[MAXN][2];
int father[MAXN];
vector<int> ve[MAXN];

void DFS( int root )
{       // 自顶向下,递归找到所有的子树然后进行第二个for循环
    for( int i=0; i<ve[root].size(); i++ )
        DFS(ve[root][i]);
    for( int i=0; i<ve[root].size(); i++ )
    {       // dp[root][0]表示不选当前子树的根节点,而选它的子节点的选中和不选中的较大值
        dp[root][0] += max(dp[ ve[root][i] ][0], dp[ ve[root][i] ][1]);
        dp[root][1] += dp[ ve[root][i] ][0];
            // dp[root][1] 表示选上当前子树的根节点,因此它的子节点不能选
    }
}

int main()
{
    while( scanf("%d", &n) != EOF )
    {
        memset(father, -1 , sizeof(father));
        memset(dp, 0, sizeof(dp));
        for( int i=1; i<=n; i++ )
        {
            scanf("%d", &dp[i][1]);
            ve[i].clear();
        }
        int u, v;   // v 是 u的主管,即父节点
        while( scanf("%d%d", &u, &v), u && v )
        {
            father[u] = v;
            ve[v].push_back(u);
        }
        int root = 1;   // 没有0 这个节点
        while( father[root] != -1 )     // 这一步是为了找到根节点,根节点不会在子节点这一列出现
            root = father[root];
        DFS(root);      // 从根节点开始递归
        printf("%d\n", max(dp[root][1], dp[root][0]));

    }


    return 0;
}