基础DP
最大字段和:
转移方程 : f[i] = max(a[i], f[i-1] + a[i])
对于要求字段起止位置的
for(int i=1;i<=T;i++){
scanf("%d", &n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int start = 1, ti = 1;
int ed = 1, ans = a[1];
f[1] = a[1];
for(int i=2;i<=n;i++){
if(f[i-1] >= 0){
f[i] = f[i-1] + a[i];
}else{
f[i] = a[i];
ti = i;
}
if(f[i]>ans){
ans = f[i];
start = ti;
ed = i;
}
}
LIS模型
-
暴力动态规划
只采用最朴素的动态规划,复杂度 O ( N 2 ) O(N^2) O(N2)
for(int i=1;i<=n;i++){ dp[i] = 1; for(int j = i-1;j;j--){ if(f[j] < f[i])dp[i] = max(dp[i], dp[j] + 1); } }
-
线段树\树状数组优化
通过线段树或者树状数组维护
[1, a[i]]
中len
的最大值,可将查询优化至 l o g n log^n logn,所以总复杂度为 O ( N l o g N ) O(Nlog^N) O(NlogN),#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int tr[50010]; int a[100010]; int n, big; int lowbit(int x) { return x & -x; } void add(int x, int k) { for (int i = x; i <= big; i += lowbit(i))tr[i] = max(tr[i], k); } int query(int x) { int res = 0; for (int i = x; i; i -= lowbit(i))res = max(res, tr[i]); return res; } int main() { while (scanf("%d", &n) != EOF) { big = 0; for (int i = 1; i <= n; i++)scanf("%d", &a[i]),big = max(big,a[i]); int len = 0; for (int i = 1; i <= n; i++) { int x = query(a[i]) + 1; len = max(len, x); add(a[i] + 1, x); } printf("%d\n", len); memset(tr, 0, sizeof tr); } }
-
二分优化
c++中二分的库函数:
lower_bound
会找出序列中第一个大于等于 x x x的数upper_bound
会找出序列中第一个大于 x x x的数用法:
lower_bound(start position, end position, x)
,函数返回位置指针算法: 定义
a[i]
为原数组的第 i i i位,f[i]
为最长上升子序列的第 i i i位,len
为最长上升子序列的长度- 判断如果
f[len] > a[i]
则找到f
中大于a[i]
的最小的数然后替换,否则把f[++len] = a[i]
- 遍历一遍后
len
即为答案
代码:
for (int i = 2; i <= n; i++) { if (f[len] > a[i]) { int* p = upper_bound(f + 1, f + 1 + len, a[i]); *p = a[i]; } else { f[++len] = a[i]; } }
- 判断如果
走格子模型: (属于基础性DP,结合题意注意边界条件即可)
加强对边界条件的敏感度
LCS及其变形:
普通LCS的转移方程为f[i][j] = max{f[i-1][j-1] + 1,f[i-1][j],f[i][j-1]};
直接写即可
而附加条件的LCS(一般为配对附加权值),如HDU-1080_Human Gene Functions
与一般的类似,但要注意边界条件以及权值的处理
//'HUD_1080'
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
int p[105][105];
void found()
{
p['A']['A']=p['C']['C']=p['G']['G']=p['T']['T']=5;
p['A']['C']=p['C']['A']=p['A']['T']=p['T']['A']=-1;
p[' ']['T']=p['T'][' ']=-1;
p['A']['G']=p['G']['A']=p['C']['T']=p['T']['C']=-2;
p['G']['T']=p['T']['G']=p['G'][' ']=p[' ']['G']=-2;
p['A'][' ']=p[' ']['A']=p['C']['G']=p['G']['C']=-3;
p['C'][' ']=p[' ']['C']=-4;
}
int main()
{
found();
int t,d[105][105],i,j,l1,l2;;
char s1[105],s2[105];
scanf("%d",&t);
while(t--)
{
scanf("%d %s",&l1,s1+1);
scanf("%d %s",&l2,s2+1);
d[0][0]=0;
for(i=1;i<=l1;i++)
{
d[i][0]=d[i-1][0]+p[s1[i]][' '];
}
for(i=1;i<=l2;i++)
{
d[0][i]=d[0][i-1]+p[' '][s2[i]];
}
for(i=1;i<=l1;i++)
{
for(j=1;j<=l2;j++)
{
d[i][j]=max(d[i-1][j]+p[s1[i]][' '],d[i][j-1]+p[' '][s2[j]]);
d[i][j]=max(d[i][j],d[i-1][j-1]+p[s1[i]][s2[j]]);
}
}
printf("%d\n",d[l1][l2]);
}
}
数字三角形(数塔问题)
简单DP,略
背包模型
完全背包问题的优化:
定义 f [ i ] [ j ] f[i][j] f[i][j]为选到第 i i i组物品,且容量为 j j j的选法的最大值
所以有 f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − v i ] + w i , f [ i − 1 ] [ j − 2 × v i ] + 2 × w i , … , f [ i − 1 ] [ j − k × v i ] + k × w i } f[i][j] = max\{ f[i-1][j],f[i-1][j - v_i] + w_i,f[i-1][j - 2\times v_i] + 2\times w_i,\dots,f[i-1][j - k\times v_i] + k \times w_i \} f[i][j]=max{f[i−1][j],f[i−1][j−vi]+wi,f[i−1][j−2×vi]+2×wi,…,f[i−1][j−k×vi]+k×wi}
又有 f [ i ] [ j − w i ] = m a x { f [ i − 1 ] [ j − v i ] , f [ i − 1 ] [ j − 2 × v i ] + w i , f [ i − 1 ] [ j − 3 × v i ] + 2 × w i , … , f [ i − 1 ] [ j − k × v i ] + k × w i } f[i][j - w_i] = max\{ f[i-1][j-v_i],f[i-1][j - 2 \times v_i] + w_i,f[i-1][j - 3\times v_i] + 2\times w_i,\dots,f[i-1][j - k\times v_i] + k \times w_i \} f[i][j−wi]=max{f[i−1][j−vi],f[i−1][j−2×vi]+wi,f[i−1][j−3×vi]+2×wi,…,f[i−1][j−k×vi]+k×wi}
因为容量不变,所以 k k k也不会发生改变
所以 f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] , f [ i ] [ j − w i ] } f[i][j] = max\{ f[i-1][j],f[i][j-w_i] \} f[i][j]=max{f[i−1][j],f[i][j−wi]}
所以可以优化掉一层循环
再加滚动数组可再优化一维数组
二维费用背包与其他背包的结合:
状态机
待更新
线性DP
待更新
状压DP
待更新
树形DP
待更新