week 7 动态规划

这周讲的动态规划,如果光看例题还以为仅仅是dfs的进阶,但是往后做就发现不同了。前者强调递归,后者强调递推。

采药

评价是完完全全就是照抄课程里的01背包,只是对象的名字变了。

思路就是在深搜的基础上(深搜时间复杂度太高),用记忆化搜索加工(现在还是递归),然后改成从前往后推(加上初始条件),就变成了dp。

代码:

#include <bits/stdc++.h>
using namespace std;
int T,M;
int t[5005],v[5005];
int res[5005][5005];
int main()
{
	cin>>T>>M;
	for(int i=1;i<=M;i++)
	{
		cin>>t[i]>>v[i];
	}
	memset(res[0],0,sizeof(res[0]));
	for(int i=1;i<=M;i++)
	{
		for(int j=0;j<=T;j++)
		{
			if(t[i]>j) res[i][j]=res[i-1][j];
			else res[i][j]=max(res[i-1][j],res[i-1][j-t[i]]+v[i]);
		}
	}
	cout<<res[M][T];
	return 0;
}

 最长上升子序列

这道题跟学习通里讲的例题有点像但有区别。例题能很清楚的理出转换式,但这道题在第二层for循环结束后要更新ans,同时要记得在第一层for循环的开始要初始化每个f[i]=1。

代码:

#include <bits/stdc++.h>
using namespace std;
int n;
int a[5005];
int f[5005]; 
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
	{
        cin >> a[i];
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
	{
        f[i] = 1;
        for (int j = 1; j < i; j++)
		{
            if(a[j] < a[i]) f[i] = max(f[i], f[j] + 1);
        }
        ans = max(ans, f[i]); 
    }
    cout << ans;
    return 0;
}

最大子段和

一看题目,有种奇怪的既视感“我是不是以前做过这道题?” 。

然后仔细想想确实做过啊,当时还不是用dp做的(当时还没学dp呢),是用贪心算法做的(甚至那个时候还没学贪心,就嗯想出来了)。

但是现在这道题数据给的很大,用贪心的话复杂度肯定爆了,只能用新学的dp。

具体思路跟上题差不多,如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列; 如果一个数加上上一个有效序列得到的结果比这个数小,那么这个数单独成为一个新的有效序列。

代码:

#include<bits/stdc++.h>
using namespace std;
int n[500005],j,ans[500005];
int main()
{
    int sum=-500005;
    cin>>j;
    for(int i=1;i<=j;i++)
    {
        cin>>n[i];
        ans[i]=max(ans[i-1]+n[i],n[i]);
        sum=max(sum,ans[i]);
    }
    cout<<sum;
    return 0;
}

LCS

这道题对本蒟蒻来说太难了, 看了好久题解里的dp表格才懵懵懂懂(好痒!有什么东西要长出来了)。

这要根据作者画的图表来理解。从上到下,从左到右分别列出两个字符串的各个字符。

第一行和第一列初始化为0,因为任何字符串和一个空串的LCS都是0;若A和B不相等,所填的值为上方或左方大的那个;若A和D不相等,所填的值为上方或左方大的那个......以此类推。

若想得到路径,则再遍历一次dp数组就好了,从最后一个位置开始往前遍历,如此直到i=0或者j=0时停止,存下来的部分即为LCS的路径。

代码:

#include <bits/stdc++.h>
using namespace std;
int dp[1010][1010];
char s1[100010], s2[100010], ans[1000010];

int main()
{
    scanf("%s%s", s1 + 1, s2 + 1);
	int len1 = strlen(s1 + 1), len2 = strlen(s2 + 1);
	for(int i = 1; i <= len1; i++)
	{
		for(int j = 1; j <= len2; j++)
		{
			dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
			if(s1[i] == s2[j])
            {
                dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
            }
		}
	}
	int i = len1, j = len2;
	while(dp[i][j] > 0)
    {
		if(s1[i] == s2[j])
        {
            ans[dp[i][j]] = s1[i];
            i--, j--;
        }
		else
        {
			if(dp[i][j] == dp[i - 1][j]) i--;
			else j--;
		}
	}
	printf("%s", ans + 1);
	return 0;
}

这就是本周所有的题目了。

在这阴被阳包围的时期,笔者本人也不幸染上阳了,写这篇博客的时候也是痛苦万分。希望风波能早点过去吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无用夜宵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值