建模思想
-
给dp[i][j]一个定义(i和j可能会有一定的关系,例如i<j)
-
再对具体某个dp[i][j]进行观察(可以借助于表格等),找出它可以由哪些已知的dp[][]经过一定的处理得到,并对这些解进行比较,得出最优解。
-
列出状态转移方程。
-
对dp初始化,再根据状态转移方程算出所有dp。
常用的状态转移方程
-
记忆化搜索dfs;
-
dp[i]=最优解{dp[j]+cost[j][i]} (0<j<i)
-
dp[i][j]为前j个地点取i个的最优解
dp[i][j]=最优解{dp[i-1][k]+cost[k+1][j]} i-1<=k<j -
二维dp的横坐标和纵坐标可能是不同的状态,取其最优解一般会有n个操作,至于dp[i][j]具体的值一般为所求值。(怎么这么像背包啊?!)
dp[i][j]=最优解(dp[i-t][j-q[k].v]+q[k].w) (0<k<n,t一般为1) -
表格dp[i][j]= 最优解{dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+1}
-
间隔dp[i]=最优解{dp[i-1],dp[i-2]+v[i]};
-
矩阵,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 | j | p | e | a | c | h | |
0 | 1 | 2 | 3 | 4 | 5 | ||
i | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
a | 1 | 0 | 0 | 0 | 1 | 1 | 1 |
p | 2 | 0 | 1 | 1 | 1 | 1 | 1 |
p | 3 | 0 | 1 | 1 | 1 | 1 | 1 |
l | 4 | 0 | 1 | 1 | 1 | 1 | 1 |
e | 5 | 0 | 1 | 2 | 2 | 2 | 2 |
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]; }