DP——板子

DP

最长上升子序列(LIS)——O(nlogn

定义
最长上升子序列是:
1:只包含ai的子序列
2:满足j<i并且aj<ai
3:子序列长度最长
朴素算法O(n^2) dp+二分(nlogn)
在这里插入图片描述
在这里插入图片描述

dp数组维护的是以pos位置结尾最小可能的值,如果要打印最长上升子序列路径,开辟新数组path,倒着找合法序列,正序寻找就会出现
1 2 4 6(2 8 6 7)这种不合法序列。虽然满足所有ai <aj 但是存在i>j

注意:严格lis:lower_bound,非严格lis:upper_bound

        vector<int>lis;
        for(int j=i;j<n+i;j++)
        {
            auto it=upper_bound(all(lis),a[j]);
            if(it==lis.end())
                lis.pb(a[j]);
            else
                lis[it-lis.begin()]=a[j];
        }

LCS(最长公共序列)——O(n^2)

枚举一下就穿上裤子就走人
在这里插入图片描述
在这里插入图片描述

for(int i=1; i<=len1; ++i)
{
    for(int j=1; j<=len2; ++j)
    {
        if(s[i-1]==t[j-1])
            dp[i][j]=dp[i-1][j-1]+1;
        else
            dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
    }
}
cout<<dp[len1][len2]<<endl;

区间DP——O(n3

一般问题
把相邻符合条件的合并,来获得最优解

概念

区间类型动态规划是线性动态规划的拓展,它在分阶段划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。(例:f[i][j]=f[i][k]+f[k+1][j])

区间类动态规划的特点:

合并:即将两个或多个部分进行整合。
特征:能将问题分解成为两两合并的形式。
求解:对整个问题设最优值,枚举合并点,将问题分解成为左右两个部分,最后将左右两个部分的最优值进行合并得到原问题的最优值。

在这里插入图片描述
DP(n^3) DP+四边形不等式优化(n^2)
一般数据500还能跑跑,如果1e3基本凉了,要优化到O(n^2),要用到四边形不等式优化。暂时不会,再等等吧

cin>>n;
memset(dp,inf);///初始化dp数组
for(int i=1; i<=n; ++i)
    cin>>a[i],pre[i]=pre[i-1]+a[i],dp[i][i]=0;
for(int len=1; len<n; ++len) ///枚举区间长度
{
    for(int i=1; i+len<=n; ++i) ///枚举区间起点
    {
        int j=i+len;///根据起点得到合法终点
        for(int k=i; k<j; ++k) ///枚举最优分割点
            dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
        dp[i][j]+=pre[j]-pre[i-1];
    }
}
cout<<dp[1][n]<<endl;

树形DP——O(m)

算法核心
出发点:点对至少需要两个点。
枚举每条边的u和v,u是v父节点,v向下的最大直径+cost+u向上的最大直径
向下的最大直径,就是看最大跟次大,但是每次先加入当前点值,保证每次直径存在尾点权
向上的最大直径,u结点先更新点权跟向上最大简单路径,再更新v的最大直径点权和
子树计数

问题:
换根理解
u到v之前,把v向上的最长路径更新一下。
取决于u向下的第一长路径是否是v

  • 如果是,dp[v][0]=max(dp[u][0],dp[u][2])+1;
  • 如果不是,dp[v][0]=max(dp[u][0],dp[u][1])+1;

每个结点换根,只需要考虑dp[v][0]是否更新,换根并不是字面意思换根,只是把dp[v][0]除了第一次dfs模型向下路径,其他路径的最长更新dp[v][0]

在这里插入图片描述

vector<int>G[N];
int a[N],dp[N],ans;
void dfs(int u,int fat)
{
    dp[u]=a[u];
    for(auto v:G[u])
    {
        if(v!=fat)
        {
            dfs(v,u);
            dp[u]+=max(0,dp[v]);
        }
    }
    ans=max(ans,dp[u]);
}

换根-任意一点能到达的最远距离

const int N=5e5+5;
vector<pair<int,int> >G[N];
int dp[N][3],pot[N];
void dfs(int u,int fat)
{
    for(auto it:G[u])
    {
        int v=it.F,cost=it.S;
        if(v==fat)
            continue;
        dfs(v,u);
        if(dp[u][0]<=dp[v][0]+cost)
        {
            dp[u][1]=dp[u][0];
            dp[u][0]=dp[v][0]+cost;
            pot[u]=v;
        }
        else if(dp[u][1]<dp[v][0]+cost)
            dp[u][1]=dp[v][0]+cost;
    }
}
void dfs1(int u,int fat)
{
    for(auto it:G[u])
    {
        int v=it.F,cost=it.S;
        if(v==fat)
            continue;
        if(pot[u]==v)
            dp[v][2]=cost+max(dp[u][1],dp[u][2]);
        else
            dp[v][2]=cost+max(dp[u][0],dp[u][2]);
        dfs1(v,u);
    }
}

状压DP——O(3n

15 or 16可以枚举子集,以上就不行了
枚举子集
例如:1011
子集1010 1001 1000 0011 0010 0001
然后xor ,就可以得到子集的补集,合并取最小即可
在这里插入图片描述
枚举子集

    for(int i=1;i<(1<<n);i++)
    {
        dp[i]=get(i);
        for(int j=i;j;j=(j-1)&i)
            dp[i]=min(dp[i],dp[j]+dp[j^i]);
    }
    cout<<dp[ (1<<n)-1 ]<<endl;

概率DP

概率顺着推,期望逆着推

高维前缀和(sosdp)—— O(nlogn)

子集前缀和

    for(int i=0;i<22;i++)
        for(int j=0;j<(1<<22);j++)
            if(j&(1<<i))
                dp[j]+=dp[j^(1<<i)];

在这里插入图片描述

数位DP

状态考虑完全,都加入的dp数组中,参考YKW模板
注意:有的时候,把limit加入dp中,每次清空dp更快,有的时候,取消limit,一次清空更快。

int dp[20][2][2][unknow],a[20];
int dfs(int pos,bool limit,bool lead,int state)
{
    if(pos==0)///边界条件
        return true or false;
    if(limit==0&&dp[pos][limit][lead][state]!=-1)
        return dp[pos][limit][lead][state];
    int maxn=(limit?a[pos]:9);
    int ans=0;
    for(int i=0;i<=maxn;i++)
    {
        if(lead&&i==0)
            ans+=dfs(pos-1,limit&&i==maxn,true,state);
        else///确定状态的改变
            ans+=dfs(pos-1,limit&&i==maxn,false,state);
    }
    if(limit==0)
        dp[pos][limit][lead][state]=ans;
    return ans;
}
int query(int x)
{
    ///这里不 容易TLE——memset(dp,-1);
    int pos=0;
    while(x)
        a[++pos]=x%10,x/=10;
    return dfs(pos,true,true,0);///开始有限制,有前导0
}
///输入样例之前清空dp

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值