动态规划(2)——常见动态规划模型

1.数字三角形

每次可以往右下或者左下走一格,求路径的最大权值.

d(i,j)=max(d(i+1,j),d(i+1,j+1))+a(i,j).边界是d(n+1,j)=0,从下往上推(因为要保证i+1行在第i行之前更新)

for(int i=1;i<=n+1;++i) d[n+1][i]=0;
for(int i=n;i>=1;--i)
{
    for(int j=1;j<=i;++j)
    {
        d[i][j]=max(d[i+1][j],d[i+1][j+1])+a[i][j];
    }
}

2.嵌套矩形

n个矩形,每个矩形用一个二元组(a,b)表示。我们规定矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d,或者b<c,a<d(旋转了90度)。选出尽量多的矩形排成一行 ,使得除了最后一个之外,每个矩形都能嵌套在下一个矩形内。若有多解,保证字典序尽量小

DAG最长路问题

//dp[i]表示的是从i点出发的最长路
int dp(int x)
{
    int &ans=d[x];
    if(ans) return ans;
    for(int i=1;i<=n;++i)
    {
        if(G[x][i])
        {
            ans=max(ans,dp(i)+1);//注意记录的是从终点到起点的距离,这是为了方便字典序最小方案的输出
        }
    }
    d[x]=ans;
    return ans;
}
void print(int i)
{
    printf("%d ",i);
    for(int j=1;j<=n;++j)
    {
        if(G[i][j]&&d[j]+1==d[i])
        {
            print(j);
        }
    }
}


for(int i=1;i<=n;++i)
{
    scanf("%d%d",&a[i],&b[i]);
    if(a[i]>b[i]) swap(a[i],b[i]);
}
for(int i=1;i<=n;++i)
{
    for(int j=1;j<=n;++j)
    {
        if(a[i]>a[j]&b[i]>b[j])
        {
            G[j][i]=1;
        }
    }
}
int Max=0;
int endpos;
for(int i=1;i<=n;++i)
{
    if(dp(i)>Max)
    {
        Max=dp[i];
        endpos=i;
    }
}
print(endpos);

3.硬币问题

f(i)=min(inf,f[iVi]+1|Vi<=i),g(i)=max(inf,g[iVi]+1|Vi<=i)(Vi)

4.01背包 已有专门专题详细讲解

5.点集配对问题(状压dp)

d(S) 表示集合S配对之后的最短距离

double dis(int i,int j)
{
    return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;++i) scanf("%d%d",&a[i].x,&a[i].y);
    memset(d,0x7f,sizeof(d));
    d[0]=0;
    for(int i=1;i<(1<<n);++i)//由于第一个点无论如何都是要配对的,所以无需枚举(否则时间复杂度会
    {                        //乘上一个n)
        int k=0;
        while(!(i&(1<<k))) ++k;
        for(int j=k+1;j<n;++j)
        {
            if(i&(1<<j)) d[i]=min(d[i],d[i^(1<<k)^(1<<j)]+dis(k,j));
        }
    }
}

6.最长上升子序列问题LIS

初级:O(n2) (不过太不优秀了,我学会了第二种,就从没用过它)

进阶:O(nlogn) d[i]表示以a[i]结尾的最长上升子序列的长度

for(int i=1;i<=n;+i) g[i]=inf;
for(int i=0;i<n;++i)
{
    int k=lower_bound(g+1,g+n+1,a[i])-g;
    d[i]=k;
    g[k]=a[i];
}

7.最长公共子序列问题(LCS)[ 注意:LCS有时也指最长公共后缀,与LCP最长公共前缀对应 ]

for(int i=0;i<la;++i)
{
    for(int j=0;j<lb;++j)
    {
        if(a[i]==b[j])
        {
            d[i][j]=max(d[i][j],d[i-1][j-1]+1);
        }
        else if(a[i]!=b[j])
        {
            d[i][j]=max(d[i-1][j],d[i][j-1]);
        }
    }
}

还可以滚动数组优化

int f=0;
for(int i=0;i<la;++i)
{
    f^=1;
    for(int j=0;j<lb;++j)
    {
        if(a[i]==b[j])
        {
            d[f][j]=max(d[f][j],[f^1][j-1]+1);
        }
        else if(a[i]!=b[j])
        {
            d[f][j]=max(d[f^1][j],d[f][j-1]);
        }
    }
}

8.最大连续和

前缀和做法:

for(int i=1;i<=n;++i)
{
    scanf("%d",&a[i]);
    sum[i]=sum[i-1]+a[i];
}
int Maxn=sum[n],Max=0;
for(int i=n-1;i>=1;--i)
{
    Max=max(Max,Maxn-sum[i]);
    Maxn=max(Maxn,sum[i]);
}

动态规划做法:d[i]表示以i结尾的最大连续和

for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
{
    d[i]=max(0,d[i-1])+a[i];
}

posted on   dolires  阅读(469)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示