DP

DP就是和列表差不多,把先前的值求出,可以由先前值推出当前值,一直推到求出答案。更准确来说是记忆化搜索。DP要划分阶段,划分后写动态转移方程。DP一般要满足两个特性:最优性与无后效性。最优性即要满足最优解性质,保证每一步都是最优的。无后效性要满足求解某个阶段只会与上一个阶段有关,或者说一个阶段求完后,不会对前面造成影响.DP的程序一般不长,但思考比较难。

下面是例题

1.51nod 1055

题面:N个不同的正整数,找出由这些数组成的最长的等差列。

样例:http://www.51nod.com/Challenge/Problem.html#!#problemId=1055 

用f[i][j]表示数列第1项为a[i],第二项为a[j]的长度,当第3项为a[k]那么f[i][j]=f[j][k]+1。且a[j]-a[i]=a[k]-a[j],变形为a[j]*2=a[i]+a[k]。因此只要枚举j,i向前扫,k向后扫可以做到O(n^2);

#include<bits/stdc++.h>
using namespace std;
int n,i,k,ans=0;
int a[11000];
short int f[11000][11000];
int main()
{
    cin>>n;
    for(int j=1;j<=n;j++)
      cin>>a[j];
    sort(a+1,a+1+n);//题目并未告知是有序数列
    for(i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      f[i][j]=2;
    for(int j=2;j<n;j++)//枚举第二项
    {
        i=j-1; k=j+1;
        while(i>=0 && k<=n)
        {
            if(a[j]*2>a[i]+a[k])//第三项太小
             k++;
            else
            {
                if(a[j]*2<a[i]+a[k])//第一项太大
                 i--;
                else
                {
                    f[j][k]=f[i][j]+1;
                    if(ans<f[j][k])//找最大
                     ans=f[j][k];
                    i--; k++;
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

2.51nod 1052 最大M子段和

题面:N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。

样例:http://www.51nod.com/Challenge/Problem.html#!#problemId=1052

f[i][j]表示前j项所构成i子段的最大和(必须包含着第j项),有两种情况 1、f[ i ][ j ] = f[ i ] [ j-1 ] + a[ j ] ,即把第j项融合到第j-1项的子段中,子段数没变 2、dp[ i ][ j ] = max(dp[ i-1 ] [ t ]) + a[ j ],(i-1<= t < j ) 把第 j 项作为单独的一个子段,然后找一下i-1个子段时,最大的和,然后加上a[ j ]。可以将t优化掉,t为上一行j以前的最大值。因此每做一次更新一下最大值。

#include<bits/stdc++.h>
using namespace std;
long long f[5][5100],a[5100],l=0,ans=0,n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
     cin>>a[i];
    memset(f,0,sizeof(f));
    for(int i=1;i<=m;i++)
    {
        l=f[(i-1)%2][i-1];//付初始值//空间小要开滚动数组:[……%2]
        for(int j=i;j<=n;j++)
        {
        /*    l=f[i-1][i-1];
            for(int k=i;k<j;k++)
             l=max(l,f[i-1][k]);*/ // /* */内为t未优化程序
            l=max(l,f[(i-1)%2][j-1]);
            f[i%2][j]=max(f[i%2][j-1]+a[j],l+a[j]);
            if(ans<f[i%2][j])
             ans=f[i%2][j];
        }
    }
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值