HDU2084_DP初步

题目链接。题目的意思就是给一个数塔,从顶部开始走到最后一层使得走过的节点数据加起来尽可能的大,求这个最大的数据。这是一个DP的经典题目。我们通过两种思路来分析。

1:我为人人DP。我们按照题目的意思来,从塔尖开始,对于第二层来说,这一层最大的数据只能是第一层加上自己当前节点的数据。那么处理好第二层我们来到第三层,第三层的每一个节点都有两种选择我们当然是选择两个中间较大的那个加到自己的节点上,这样第三层又被更新,接着来到第四层,使用同样的方法,我们更新第四层的数据,一直下去,我们一直来到底层,这样底层的数据被更新,那么可以确定的是最大值一定在最底层,那么我们遍历最后一层数据就可以找到最大值。这样其实我们的思想就是使用上层的数据一直来更新下一层的数据。每一个非最底层的数据其实是在为他的下一层做出贡献,所以我们把这种DP就做我为人人的DP。细节的分析:我们采用一个二维的数组作为DP数组dp[i][j]的含义当前的节点是第i层第j个。

转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j](a是保存节点数据的数组)。解释一下这个转移方程,当前第i层第j个元素的值一定是由他的上边的一层的元素转移过来的,转移的规则肯定是选择两个子节点中较大的那一个然后加上自己当前节点的值。这样我们对于每一层都进行DP,那么每一层都是保存着当前层的最大值,一直到最后一层,最大值就会被更新到最底层。这样我们只需要遍历最后一层取出最大的元素即可。代码实现如下:

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 105;
int ans[maxn][maxn];
int dat[maxn][maxn];
int main()
{
	int T;
	int dep;
	while (cin >> T)
	{
		for (int i = 1; i <=T; i++)
		{
			cin >> dep;
			for (int i = 1; i <=dep; i++)
			{
				for (int j = 1; j <=i; j++)
				{
					cin >> dat[i][j];
				}
			}
			ans[1][1] = dat[1][1];
			for (int i = 2; i <=dep; i++)
			{
				for (int j = 1; j <=i; j++)
				{
					ans[i][j] = dat[i][j] + max(ans[i - 1][j], ans[i - 1][j - 1]);
				}
			}
			int max_an = 0;
			for (int i = 1; i <=dep; i++)
			{
				max_an = max(max_an, ans[dep][i]);
			}
			cout << max_an << endl;
		}

	}
		return 0;
}

人人为我的DP。在上边的解法中,其实我们看到的是我们最习惯的解法,也就是我们从起点开始,逐层逐层的向下走,通过上层的数据更新当前节点的值,这样我们就可以把最大值更新到最底层,之后我们再扫描最底层就可取得最大值。所有的非最底层的数据的作用其实都是为了更新他的下一层数据。我们换一种方式考虑,其实这个最优的路径是确定的,我们既然可以从上向下逐层的递推,反过来肯定也是可以的。因为路径是确定的,我们从底层开始,使用该层的数据去更新它的上一层数据,然后从下岛上DP,这样最大值被一层一层的向上递推,知道到达顶点。可以确定的是,最大值一定是被递推到了这个树根。这样我就没有必要再去遍历,直接输出树根当前的DP值即可。具体的实现如下:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 105;
int ans[maxn][maxn];
int dat[maxn][maxn];
int main()
{
    int T;
    int dep;
    while (cin >> T)
    {
        for (int i = 1; i <=T; i++)
        {
            cin >> dep;
            for (int i = 1; i <=dep; i++)
            {
                for (int j = 1; j <=i; j++)
                {
                    cin >> dat[i][j];
                }
            }
            memset(ans, 0, sizeof(ans));
            for (int i = dep; i >= 1; i--)
            {
                for (int j = 1; j <=i; j++)
                {
                    ans[i][j] = max(ans[i + 1][j], ans[i + 1][j + 1]) + dat[i][j];
                }
            }
            cout << ans[1][1] << endl;
        }

    }
        return 0;
}

以上就是DP中比较简单的两个题目,虽然简单但是这种转移的思想以及两种DP方式可以被推广的十分的广泛,例如在树形数据结构上进行DP就是树形DP,在树形DP上我们可以由这两种DP方式演化出由父节点向子节点DP,或者由子节点向父节点DP。以及可以将通过将状态压缩成一个整数二进制数据的状压DP,还有区间DP,概率DP等等。都是一个思想。由当前状态转移到下一个状态的转移方式,解决了它就解决了DP的主要矛盾。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值