dp小结

建模思想

  1. 给dp[i][j]一个定义(i和j可能会有一定的关系,例如i<j)

  2. 再对具体某个dp[i][j]进行观察(可以借助于表格等),找出它可以由哪些已知的dp[][]经过一定的处理得到,并对这些解进行比较,得出最优解。

  3. 列出状态转移方程。

  4. 对dp初始化,再根据状态转移方程算出所有dp。

常用的状态转移方程

  1. 记忆化搜索dfs;

  2. dp[i]=最优解{dp[j]+cost[j][i]} (0<j<i)

  3. dp[i][j]为前j个地点取i个的最优解
    dp[i][j]=最优解{dp[i-1][k]+cost[k+1][j]} i-1<=k<j

  4. 二维dp的横坐标和纵坐标可能是不同的状态,取其最优解一般会有n个操作,至于dp[i][j]具体的值一般为所求值。(怎么这么像背包啊?!)
    dp[i][j]=最优解(dp[i-t][j-q[k].v]+q[k].w) (0<k<n,t一般为1)

  5. 表格dp[i][j]= 最优解{dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+1}

  6. 间隔dp[i]=最优解{dp[i-1],dp[i-2]+v[i]};

  7. 矩阵,dp:化为一维,left[i],right[i];

注意

找规律的一些dp可以把完整的一遍先算完,避免TLE。例HUD 1502

常见类型

记忆化搜索

int dfs(int S)
{
    if(dp[S]!=-1) return dp[S];   //记得初始化为-1
    dp[S]=-1<<30;
    int i;
    for(i=1;i<=n;i++)
    {
        if(S>=v[i])
        {
            if(dp[S]<dfs(S-v[i])+w[i])
            {
                dp[S]=dfs(S-v[i])+w[i];
            }
        }
    }
    return dp[S];
}

最大连续子序列

跟前一个比较,sum<0从头开始记,不然++

代码
sum=0;  
for (i=0;i<n;i++)  
{  
     if (sum<0)  
        sum=a[i];  
     else  
        sum+=a[i];  
}  

最长公共子序列

dp jpeach
  012345
i0000000
a1000111
p2011111
p3011111
l4011111
e5012222

dp[i][j]= max(dp[i-1][j],dp[i][j-1])
dp[i][j]= max[dp[i][j], dp[i-1][j-1]+1,(str1[i]==str2[j])]

找出最长公共子序列(回溯)
int k=0;
char q[110];
int i=strlen(str1);
int j=strlen(str2);
while(i!=0||j!=0)
{
    if(i==0) q[k++]=str2[j--];                      //str2还有多
    else if(j==0) q[k++]=str1[i--];                 //str1还有多
    else if(dp[i][j]==dp[i][j-1]) q[k++]=str2[j--]; //从左边取值
    else if(dp[i][j]==dp[i-1][j]) q[k++]=str1[i--]; //从右边取值
    else if(dp[i][j]==dp[i-1][j-1]+1)               //从左上角取值
    {
        q[k++]=str1[i--];
        j--;
    }
}

最长上升子序列

常规做法
for(i=0;i<n;i++)
{
    int max=0;
    for(j=0;j<i;j++)
    {
        if(v[i]>v[j]&&dp[i]<dp[j]+1) dp[i]=dp[j]+1;
    }
}
优化
int dp[MAXN];    //dp长度,dp[i]为当前i长度最小的v[k];
for(i=1;i<n;i++)
{
    l=1;r=len;
    while(l<=r)    //二分找到v[i]最优位置
    {
        mid=(l+r)/2;
        if(dp[mid]<v[i]) l=mid+1;
        else r=mid-1;
    }
    dp[l]=v[i];   //更新dp[l];
    if(l>len) len++;   //增加长度
}

最长公共递增子序列

n^2做法

        int temp=0;
        for(i=1;i<=n;i++)
        {
            for(temp=0,j=1;j<=m;j++)
            {
                if(v1[i]>v2[j]&&dp[j]>temp) temp=dp[j];
                if(v1[i]==v2[j]) dp[j]=temp+1;
            }
        }
        temp=0;
        for(i=1;i<=m;i++)
            temp=max(dp[i],temp);

dp==lr

for(i=1;i<=n;i++)  
{  
     while(v[l[i]-1]>=v[i])  
              l[i]=l[l[i]-1];  
}  
for(i=n;i>=1;i–-)  
{  
     while(v[r[i]+1]>=v[i])  
              r[i]=r[r[i]+1];  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值