算法笔记------DP

基础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为最长上升子序列的长度

    1. 判断如果f[len] > a[i]则找到f中大于a[i]的最小的数然后替换,否则把f[++len] = a[i]
    2. 遍历一遍后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[i1][j],f[i1][jvi]+wi,f[i1][j2×vi]+2×wi,,f[i1][jk×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][jwi]=max{f[i1][jvi],f[i1][j2×vi]+wi,f[i1][j3×vi]+2×wi,,f[i1][jk×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[i1][j],f[i][jwi]}

所以可以优化掉一层循环

再加滚动数组可再优化一维数组

二维费用背包与其他背包的结合:

状态机

待更新

线性DP

待更新

状压DP

待更新

树形DP

待更新

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值