openjudge动规刷题攻略---第2弹

本文介绍了动态规划在解决编程竞赛题目中的应用,包括01背包问题、最长公共子序列、斐波那契数列求和及最长上升子序列问题。通过实例解析动态转移方程,提供不同复杂度的算法实现,帮助读者提升动态规划能力。
摘要由CSDN通过智能技术生成

基本算法之动态规划(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;

}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值