dp小结

来源:互联网 发布:java snmp4j get超时 编辑:程序博客网 时间:2024/06/05 17:04

建模思想

  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  012345i0000000a1000111p2011111p3011111l4011111e5012222

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];  }  
0 0
原创粉丝点击