7/3刷题日记-dp入门

重拾算法第一天,选择从dp开始入门。

对于dp(动态规划)的理解:dp算法高效的原因在于记忆化,dp算法适用于解决具有最优子结构,重叠子问题,无后效性的问题。这时候记忆化的优势就可以体现出来,具体来说就是由于存在重叠子问题,可以把子问题的解保存下来,在求解其他问题时不必再求,可以减少时间复杂度。其实递推求解也是一种特殊的dp,特殊在于,递推的子结构只有一种情况。而dp来说,是要通过比较找到子问题的最优子结构(子问题的最优解)。

用dp解题的关键在于找到状态转移方程,这非常重要。

以上都是个人理解,不保真。

dp入门题:

hdu-2084 数塔

在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的:

有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

这道题是dp的模板题,从dp的三个特点来分析,重叠的子问题:对于一个5层数塔来说,要求得它的最优解是找到它对应的四层数塔的最优解,这就是重叠子问题。最优子结构:求解一个n层的数塔的最优解,我们将n层数塔对应的最优解设为f[n],那么就有f[n]=f[n-1]+该节点的值。那么f[n-1]又可以递归下去,当n=1时,f[1]就是节点的值。无后效性:显然是不存在后效性的。

具体实现:

维护一个二维数组dp[][],用于保存当前节点对应的数塔的最优解,由最优子结构,我们显然是从数塔底部向上维护dp数组。 

        for(int j=1;j<=n;j++)
		{
			dp[n][j]=a[n][j];//一层数塔最优解是节点的值
		}
		for(int i=n-1;i>=1;i--)
		{
			for(int j=1;j<=i;j++)
			{
				dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]);//选择最优子结构,并存入dp数组中
			}
		}

 

hdu-1176 免费馅饼

这道题可以说是数塔的变形题,只需要通过预处理将输入变成与数塔类似的二维数组里。然后的dp【】【】数组的构建思路和数塔完全相同,不过需要注意边界的状态转移方程略有不同。

hdu-1257 最少拦截系统

本题实际上也可以用贪心的思路做,但是根据Dilworth定理,可以将问题转化为求最长上升子序列,用dp求解最长上升子序列是非常简便的。

        int ma=0;
		for(int i=0;i<n;i++)
		{
			f[i]=1;
			for(int j=0;j<i;j++)
			{
				if(a[j]<a[i])
				{
					f[i]=max(f[i],f[j]+1);
				}
			}
			ma=max(ma,f[i]);
		}
		printf("%d\n",ma);

hdu-1160 FatMouse's Speed

本题实质就是求最长下降子序列,但是需要降维和记录dp的路径,(有点难。。。),降维可以用sort排序实现,记录路径是通过构造一个pre【】数组,记录某个点的前驱,最后可以使用递归输出来实现倒着输出,(这是我的for循环设计的问题,导致我要记录的是前驱)(思路确实巧妙)

附上代码:

#include<bits/stdc++.h>

using namespace std;

struct node
{
	int n;
	int wei;
	int sp;
} a[1001];

int f[1001];
int pre[1001]={0};
void output(int pos)
{
	if(pos==0)
		return;
	output(pre[pos]);
	printf("%d\n",a[pos].n);
}

bool cmp(node a, node b)
{
	if(a.wei==b.wei)
		return a.sp>b.sp;
	return a.wei<b.wei;
}

int main()
{
	int count=0;
	while(cin>>a[count].wei>>a[count].sp)
	{
		a[count].n=count+1;
		count++;
	}
	sort(a,a+count,cmp);
	int res=0;
	int id=0;
	for(int i=0; i<count; i++)
	{
		f[i]=1;
		for(int j=0; j<i; j++)
		{
			if(a[i].sp<a[j].sp&&a[i].wei>a[j].wei)
			{
				if(f[i]<f[j]+1)
					pre[i]=j;
				f[i]=max(f[i],f[j]+1);
			}
		}
		if(res<f[i])
		{
			res=f[i];
			id=i;
		}
	}
	
	printf("%d\n",res);
	output(id);
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值