这周讲的动态规划,如果光看例题还以为仅仅是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;
}
这就是本周所有的题目了。
在这阴被阳包围的时期,笔者本人也不幸染上阳了,写这篇博客的时候也是痛苦万分。希望风波能早点过去吧。