1、采药
思路:
经典且简单的动态规划题。我们可以考虑用记忆化搜索的方法。
记忆化搜索的定义:
记忆化搜索是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。
因为记忆化搜索确保了每个状态只访问一次,它也是一种常见的动态规划实现方式。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int t,m;
struct herb //表示一个草药
{
int x; //采这个草药需要的时间
int value; //这个草药的价值
}a[105];
int vis[105][1005];
//vis[i][j]用于保存所给数据中第i个草药在剩余j时间情况下的dfs计算结果
int dfs(int b,int c)
{
if(vis[b][c]!=-1)return vis[b][c];//计算过就直接返回原来计算过的结果
int ans=0;
if(b==0)return 0; //dfs出口
else if(a[b].x>c)return dfs(b-1,c); //不能采这个草药的情况
else ans=max(dfs(b-1,c),dfs(b-1,c-a[b].x)+a[b].value);
//能采则选择采或不采两种情况,选择草药价值更大的
return vis[b][c]=ans; //返回采前b个草药能获得的最大价值
}
int main()
{
memset(vis,-1,sizeof(vis)); //全初始化为-1,以表示已经计算过该结果
cin>>t>>m;
for(int i=1;i<=m;i++)
cin>>a[i].x>>a[i].value;
cout<<dfs(m,t);
return 0;
}
2、最长上升子序列
思路:
这题我一开始是想用记忆化搜索的方法做的,但发现106数据太大了二维数组开不了那么大,所以我换用了状态转移的方法,有点像递归法。这题毕竟经典,直接上代码理解。
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n;
cin>>n;
int a[n+1];
int res[n+1]; //res[i]表示以a[i]结尾时最长上升子序列的长度
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
res[i]=1;//以a[i]结尾的最长上升子序列至少有一个a[i],所以初始为1
for(int j=1;j<i;j++) //到a[i]前面找比a[i]小的数
{
if(a[j]<a[i])res[i]=max(res[i],res[j]+1);
}
}
sort(res+1,res+1+n); //只需要找到res数组中最大的元素即可
cout<<res[n];
return 0;
}
3、最大子段和
思路:
这题跟上一题很像,哦不,可以说是基本上一模一样。直接看代码。
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n;
cin>>n;
int a[n+1];
int res[n+1]; //res[i]表示必须以a[i]结尾的最大子段和
for(int i=1;i<=n;i++)
cin>>a[i];
res[1]=a[1];
for(int i=2;i<=n;i++)
{
res[i]=max(a[i],res[i-1]+a[i]);
//这个地方有点不好理解,需要仔细推敲
}
sort(res+1,res+1+n);
cout<<res[n];
return 0;
}
4、最长公共子序列
思路:
这题有许多做法,但是应该思想都差不多,这里给出一个腾讯开发社区的大佬的代码,简洁明了。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
char str1[1000], str2[1000];
int length[1000][1000]; //str1的左边i个字符形成的子串,与str2左边的j个字符形成的子串的最长公共子序列的长度(i, j从0 开始算),
int main(void)
{
while (cin>>str1>>str2)
{
int len_one = strlen(str1), len_two = strlen(str2), i, j;
for (i = 0; i <= len_one; i++)
length[i][0] = 0; //str1前i个字符的字串串,str2为0,字串为0
for (j = 0; j <= len_two; j++)
length[0][j] = 0; //str2前j个字符的字串串,str2为0,字串为0
for(i=1;i<=len_one;i++)
for (j = 1; j <= len_two; j++)
{
if (str1[i - 1] == str2[j - 1]) //如果两者相等。此时这个字符是两个字符串的一个LCS
length[i][j] = length[i - 1][j - 1] + 1;
else //如果两者不等,那么这个字符可能是length[i - 1][j], length[i][j - 1]其中一个的一个LCS
length[i][j] = max(length[i - 1][j], length[i][j - 1]);
}
cout << length[len_one][len_two] << endl;
}
return 0;
}