清北学堂2019.7.17

Day 5 钟皓曦(这里就不得不安利一下之前上的钟神的dp课程了【屠龙宝刀点击就送】)

Dp(动态规划)
斐波那契数列->

  1.用别人算自己,复杂度O(n)

  2.用自己算别人,复杂度O(n)

  3.运用记忆化搜索
会存在一些题,几种代码的难度差别很大
无后效性:动态规划所有状态之间组成了一个有向无环图

     如果会乱序,那么拓扑排序,之后for一边
状态:所算的东西
转移方程:算的方法
《背包九讲》
一。背包dp
n个物品,m个容器,每个物品都其体积与价值,求最大的价值
(01背包)
经典问题:采药

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iomanip>
#include<string>
#include<algorithm>
#include<cstdlib>
using namespace std;
int dp[10000000],v[1000],p[1000];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int a=1;a<=m;a++)
    {
        cin>>v[a]>>p[a];
    }
    for(int j=1;j<=m;j++)
    {
        for(int i=n;i>=v[j];i--)
        {
                dp[i]=max(dp[i],dp[i-v[j]]+p[j]);
        }
    }
    cout<<dp[n];
    return 0;
}

转移方程:f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])(前提j>=v[i])
在最后的时候,还要用ans=0再来更新一边答案,保证答案正确

(完全背包)
经典问题:疯狂采药

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iomanip>
#include<string>
#include<algorithm>
#include<cstdlib>
using namespace std;
int dp[10000000],v[10001],p[10001];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int a=1;a<=m;a++)
    {
        cin>>v[a]>>p[a];
    }
    for(int j=1;j<=m;j++)
    {
        for(int i=v[j];i<=n;i++)
        {
                dp[i]=max(dp[i],dp[i-v[j]]+p[j]);
        }
    }
    cout<<dp[n];
    return 0;
}


(有限背包)
把多个物品转化成捆绑包,则问题转化成了01背包

这里并没有找到切实际的题目,还是看背包九讲吧qwq
二。基础dp
经典例题:数字三角形

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int f[1001][1001],a[1001][1001],n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
         cin>>a[i][j];
    }
    f[1][1]=a[1][1];
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j];
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,f[n][i]);
    }
    cout<<ans;
    return 0;
    }

数字三角形2(思路↓)
f[i][j][k]走到第i行第j列mod100==k是否成立【bool型】
最长不上升子序列:可以用线段树加以优化


三。区间dp
经典例题:石子合并(这种问题一看就是区间dp【算是一种区间dp的应用】)
f[l][r]=min(f[l][p]+f[p+1][r]+sum[r]-sum[l-1])【l<=p<r】
问题:矩阵乘法,自定义顺序,是的运算次数最少
f[l][r]表示把第l个矩阵到第r个矩阵合并成一个矩阵所用的最少次数
同样枚举分界点
 f[j][i]=min(f[j][i],f[j][k]+f[k+1][i]+a[j]*a[k+1]*a[i+1])


四。状压dp
平面上有n个点,第i个点的坐标为(x[i],y[i]),要求从一号点出发,把所有点都走一遍之后回到一号点,求解最短的一条路径
f[s][i]表示在i边走过了s个点
状压:将数字代表数组:经过哪个点,对应二进制为1,反之为0。
代码8(TSP旅行商P1171)    


洛谷P1879
农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
f[i][s]前i行的草已经行种成s样(种成什么模样)也是压缩成一个单独的状态【二进制位上不能有连续的两个1】
五。数位dp
f[i][j]表示已经从最高位向低位填了i为,j=0表示填的数小于x,j=1表示刚好等于x
枚举下一位填什么
一般会把[l,r]看成[0,r]-[0,l-1]
数位dp枚举位数的复杂度大概是O(log n)
求在[L,R]中的数的数位之和
思路:f[i][j]表示做到第i位是否顶上界的数位之和
求解:[l,r]中有多少个相邻两数之差至少为2的数?
多加一个维度
f[i][j][k]:已经填到了第i位,j表示前面的是小于还是等于,k表示第i位为k
windy 数(选做)
求在[L,R]中的满足各位数字之积为K的数有多少个                      L,R <=10^18
用六维dp:f[i][j][a][b][c][d] a表示2的幂数 b表示3的幂数 c表示5的幂数 d表示7的幂数
bzoj 2757 Blinker的仰慕者
但是还需要优化qwq
找出仅以2,3,5,7为因子的数,打表(搜索)然后降维
f[i][j][k]k表示第k个这样的数
六。树形dp(有根树才能跑)
求树上有多少个点
求树的直径
从LCA的最长和次长
f[i][0]表示从i向下走,最长走多少,f[i][1]表示从i向下走,次长走多少
求树上路径总长度和
f[i]表示以i为根的子树有多少个点
Σ2*f[i]*(n-f[i])
P1352 没有上司的舞会
f[i][j] j=0没选 j=1选了
max(f[1][0],f[1][1])
若i选了,则i的儿子都不能选
f[i][1]=Σ(f[j][0])+v[i]       j∈son[i]
f[i][0]=Σmax(f[j][0],f[j][1]) j∈son[i]
洛谷P2016
f[i][0]=Σ(f[j][0])+v[i]       j∈son[i]
f[i][1]=Σmax(f[j][0],f[j][1]) j∈son[i]
如果守护结点不能距离超过二(消防局的设立)
f[i][j] j表示在i这棵子树被完全的情况下,往下走走到的最近的士兵的距离
f[i][0]=Σmin(f[j][0/1/2])+1   j∈son[i]
g[j][0/1] j 表示已经确定了前j个儿子的取值 0/1表示有没有一个儿子有士兵
f[i][1] 还需要再套一个dp
f[i][2] zhx咕咕咕

原文地址:https://www.cnblogs.com/gongcheng456/p/11202896.html