基本算法之动态规划(5~8)
1775 采药
解题思路
裸的01背包DP,没啥好说的……
for (int i=1;i<=m;i++)
for (intj=v;j>=t[i];j--)
f[j]=max(f[j],f[j-t[i]]+val[i]);
1808公共子序列
解题思路
和编辑字符串距离差不多的题……
Point.1 字符串处理
因为循环过程中涉及到f[-1],众所周知c++数组下标呵呵,于是。。。要么在原字符串前面加个奇怪的符号,要么把字符串拆了错一位放到字符数组里,各位客官看着办吧;读入也要注意。
Point.2 动态转移方程
F[i][j]表示两个字符串分别到第I、j位的最长公共子序列长度,由此可以想到:
当a[i]==b[j]时,f[i][j]=f[i-1][j-1]+1;
a[i]!=b[j]时,在f[i][j-1]和f[i-1][j]里取较大值。
while(cin>>s1>>s2)
{
s1='a'+s1;
s2='b'+s2;
memset(f,0,sizeof(f));
for (inti=1;i<s1.length();i++)
for (intj=1;j<s2.length();j++)
{
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(s1[i]==s2[j])
f[i][j]=f[i-1][j-1]+1;
}
printf("%d\n",f[s1.length()-1][s2.length()-1]);
}
1944 吃糖果
其实就是一个斐波那契数列求和,完全是递推的思想……
1996 登山
正着、倒着跑两遍最长上升子序列。就这个题来说n^2的算法更好一点,但也可以练练手写一下nlogn的。
n^2写法
for (int i=1;i<=n;i++)
{
f1[i]=1;
f2[i]=1;
}
for (inti=1;i<=n;i++)
for (intj=1;j<i;j++)
if(f1[j]+1>f1[i]&&a[j]<a[i])
f1[i]=f1[j]+1;
for (int i=n;i;i--)
for (intj=n;j>i;j--)
if(f2[j]+1>f2[i]&&a[i]>a[j])
f2[i]=f2[j]+1;
ans=0;
for (inti=1;i<=n;i++)
{
if(f1[i]+f2[i]-1>ans)
ans=f1[i]+f2[i]-1;
}
cout<<ans;
nlogn写法:
int f1[1005],f2[1005],a[1005],s[1005];
int ef1(int l,int r,int st)
{
while (l<=r)
{
intmid=(l+r)/2;
if(s[mid]<st)l=mid+1;
else r=mid-1;
}
return l;
}
int ef2(int l,int r,int st)
{
while (l<=r)
{
intmid=(l+r)/2;
if(s[mid]>st) l=mid+1;
else r=mid-1;
}
return l;
}
int main()
{
int n,ans,pos;
cin>>n;
for (inti=1;i<=n;i++)
cin>>a[i];
for (intk=1;k<=n;k++)
{
memset(s,0x3f,sizeof(s));
ans=0;
for (inti=1;i<=k;i++)
{
pos=ef1(1,i,a[i]);
s[pos]=min(s[pos],a[i]);
if(ans<pos) ans=pos;
}
f1[k]=ans;
ans=0;
memset(s,-127,sizeof(s));
for (inti=1;i<=n-k+1;i++)
{
pos=ef2(1,i,a[i+k-1]);
s[pos]=max(s[pos],a[i+k-1]);
if(ans<pos) ans=pos;
}
f2[k]=ans;
}
ans=0;
for (inti=1;i<=n;i++)
{
if(f1[i]+f2[i]-1>ans)
ans=f1[i]+f2[i]-1;
}
cout<<ans;
return 0;
}