Week7 简单DP
第一题(采药)
评析:仔细一看,就是照搬了一下01背包,连数据都不换,这里用了记忆化搜索来做
#include<bits/stdc++.h>
using namespace std;
int T,m,t[105],v[105],vis[105][1005];
int dfs(int i,int j)
{
int ans=0;
if (vis[i][j]!=-1) return vis[i][j];
if (i==0) ans=0;//如果时间为0,那显然我们无法采药
else if (j<t[i]) ans=dfs(i-1,j); //当我们取这个数时,要考虑这个数是不是大于剩余时间,这样也就避免了下一句中负数的出现
else ans=max(dfs(i-1,j),dfs(i-1,j-t[i])+v[i]);//dfs核心语句
vis[i][j]=ans;//标记
return ans;
}
int main()
{
cin>>T>>m;
memset(vis,-1,sizeof(vis));
for (int i=1;i<=m;i++) cin>>t[i]>>v[i];
cout<<dfs(m,T);
}
第二题(最长上升子序列)
评析:一维较简单的DP,基本思路就是我们看是从现在接着往后,还是从后往前找到当时最长的子序列后接着往下
#include<bits/stdc++.h>
using namespace std;
int n,ans;
int a[1000005],f[1000005];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
f[1]=1;
for(int i=2;i<=n;i++)
for(int j=i-1;j>=0;j--)//从后往前找
if(a[i]>a[j])
{
f[i]=max(f[j]+1,f[i]);//比较从哪里往后接下去
}
for(int i=1;i<=n;i++) ans=max(ans,f[i]);//比对答案
cout<<ans;
return 0;
}
第三题(最大子段和)
评析:就是用一个sum记录当前前缀和,如果前缀和sum变成了负数,那么说明前面太小了,我们就从这个数开始,这时把sum置为0,再继续累加。
#include<bits/stdc++.h>
using namespace std;
int n,a,sum,ans=-10000;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
if(i==1) sum=a;
else sum=max(a,sum+a);
ans=max(ans,sum);
}
cout<<ans;
return 0;
}
第四题
评析:LCS模板+双指针追踪,输出路径
#include<bits/stdc++.h>
using namespace std;
char s[3005],t[3005],ans[3005];
int n,m,f[3005][3005];
int main()
{
scanf("%s",s+1);
scanf("%s",t+1);//小操作,可以让输入的字符串从1开始,操作比较方便
n=strlen(s+1);
m=strlen(t+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
f[i][j]=max(f[i-1][j],f[i][j-1]);
if (s[i]==t[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}//LCS板子
int i=n, j=m;//双指针
while(f[i][j] > 0)
{
if(s[i]==t[j])
{
ans[f[i][j]] = s[i];//记录重复的
i--;
j--;
}
else
{
if(f[i][j]==f[i - 1][j]) i--;
else j--;
}
}
printf("%s", ans+1);
return 0;
}