动态规划(冬令营课堂笔记)

 

正文

 

简单问题

  01背包

  012背包

  部分背包

  机器分配

  烽火传递

  花店橱窗问题

简单问题

01背包

一个容量为m的背包,有n个物品,第i个物品的体积为wi,价值为ci。选择若干物品,使得体积总和不超过m的情况下价值总和最大。

n<=100,m<=10000。

 

搜索  复杂度为2^n

复制代码
void dfs(int x,int y,int z)//当前物品编号,当前容量,当前价值
{
    if(x==n+1)
    {
        if(y<m)ans=max(ans,z);
        return;
    }
    dfs(x+1,y+w[x],z+c[i]);
    dfs(x+1,y,z);
}
复制代码

如果前两维相同,只需选择第三维的最大值

小小的变形

复制代码
const int INF=0x7f;
int dfs(int x,int y)
{
    int ans;
    if(x==n+1)
    {
        if(y>m)return -INF;
        return 0;
    }
    ans=max(dfs(x+1,y+w[x])+c[x],dfs(x+1,y));
    return ans;
}
dfs(1,0);
复制代码

加一个数组记录

复制代码
const int INF=0x7f;
int dfs(int x,int y)
{
    if(x==n+1)
    {
        if(y>m)return -INF;
        return 0;
    }
    dp[x][y]=max(dfs(x+1,y+w[x])+c[x],dfs(x+1,y));
    return dp[x][y];
}
dfs(1,0);
复制代码

应用记录状态  记忆化搜索 n*m

复制代码
const int INF=0x7f;
int dfs(int x,int y)
{
    if(y>m)return -INF;
    if(x==n+1)
    {
        return 0;
    }
    if(dp[x+1][y+w[x]])             //选该物品 
    dp[x][y]=max(dp[x+1][y+w[x]]+c[x],dp[x][y]);
    else
    dp[x][y]=max(dfs(x+1,y+w[x])+c[x],dp[x][y]); 
    
    if(dp[x+1][y])             //不选该物品 
    dp[x][y]=max(dp[x+1][y]+c[x],dp[x][y]);
    else
    dp[x][y]=max(dfs(x+1,y))+c[x],dp[x][y]);     
    return dp[x][y];
}
dfs(1,0);
复制代码

递归变递推

先写出搜索

最大/最小值 存放到数组中

后面递归该状态时 直接使用数组中的值来代替

O(dfs里的状态个数*转移的时间复杂度)

 

找边界

找递推方法(顺序还是逆序)

转移(推转移方程)

搜索->记忆化搜索->递归变递推

for(int i=n;i>=1;i--)
     for(int j=0;j<=m;j++)
         dp[i][j]=max(dp[i+1][j],dp[i+1][j+w[i]]+c[i]);
ans=dp[1][0]

012背包

一个容量为m的背包,有n个物品,第i个物品的体积为wi,价值为ci,有2个。

选择若干物品,使得体积总和不超过m的情况下价值总和最大。

记忆化搜索

复制代码
const int INF=0x7f;
int dfs(int x,int y,int z)
{
    if(x==n+1){if(y<m)ans=max(ans,z);return -INF;}
    
    if(dp[x+1][y])                    //一个都不选 
    dp[x][y]=max(dp[x][y],dp[x+1][y]);
    else
    dp[x][y]=max(dp[x][y],dfs(x+1,y));
    
    if(dp[x+1][y+w[x]])                //选一个 
    dp[x][y]=max(dp[x][y],dp[x+1][y+w[x]])+c[x]);
    else
    dp[x][y]=max(dp[x][y],dfs(x+1,y+w[x])+c[x]);
    
    if(dp[x+1][y+w[x]])                //选两个 
    dp[x][y]=max(dp[x][y],dp[x+1][y+2*w[x]])+2*c[x]);
    else
    dp[x][y]=max(dp[x][y],dfs(x+1,y+2*w[x])+2*c[x]);
    
    return dp[x][y];
}
dfs(1,0); 
复制代码

部分背包

一个容量为m的背包,有n个物品,第i个物品的体积为wi,价值为ci,有ki个。

选择若干物品,使得体积总和不超过m的情况下价值总和最大。

记忆化搜索

复制代码
int dfs(int x,int y)
{
    if (y>m) return -INF;
    if (x==n+1) return 0;
    for (int i=0; i<=k[x]; i++)
    {
        if (dp[x+1][y+i*w[x]])
            dp[x][y]=max(dp[x][y],dp[x+1][y+i*w[x]]+c[x]);
        else
              dp[x][y]=max(dp[x][y],dfs(x+1,y+i*w[x])+i*c[x]);
    }
    return dp[x][y];
}
dfs(1,0);
复制代码

递推

for (i=n; i>=1; i--)
    for (j=0; j<=m; j++)
    for (l=0; l<=k[i]; l++)
    dp[i][j]=max(dp[i][j],dp[i+1][j+l*w[i]]+l*c[i]);
dp[1][0]

机器分配

有n家店,每家店都有m台机器,第i家店购买j台机器花费a[i][j]元(可能存在a[i][j]>a[i][j+1]),要购买总共m台机器,求最小花费。

搜索

复制代码
//机器分配 
dfs(int x,int y,int z)
{
    if(y>m)return;
    if(x==n+1)
    {
        if(y==n+1)ans=min(ans,z);
        return;
    }
    for(int i=0;i<=m;i++)
        dfs(x+1;y+i,z+a[x][i]);
}
dfs(1,0,0);
复制代码

小小的变形

复制代码
//机器分配 
int dfs(int x,int y)
{
    if(y>m)return -INF;
    if(x==n+1)
    {
        if(y==m)return 0;//再也不需要买机器 
        return -INF;
    }
    for(int i=0;i<=m;i++)
    dp[x][y]=min(dp[x][y],dfs(x+1,y+i)+a[x][i]);
    return dp[x][y];
}
dfs(1,0);
复制代码

记忆化搜索

复制代码
//机器分配 
int dfs(int x,int y)
{
    if(y>m)return -INF;
    if(x==n+1)
    {
        if(y==m)return 0;//再也不需要买机器 
        return -INF;
    }
    for(int i=0;i<=m;i++)
    if(dp[x+1][y+i])
    dp[x][y]=min(dp[x][y],dp[x+1][y+i]+a[x][i]);
    else
    dp[x][y]=min(dp[x][y],dfs(x+1,y+i)+a[x][i]);
    return dp[x][y];
}
dfs(1,0);
复制代码

递推

for(int i=n;i>=1;i--)
    for(int j=0;j<=m;j++)
    {
        dp[i][j]=INF;
        for(k=0;k<=m-j;k++)
        dp[i][j]=min(dp[i][j],dp[i+1][j+k]+a[i][k]);
    }
ans=dp[1][0]

烽火传递

给定n个非负整数,选择其中若干数字,使得每连续k个数中至少有一个数被选出。

要求选择的数字之和尽可能小。

搜索

void dfs(int x,int y)
{
    if(x==n+1){ans=min(ans,y);return;}
    for(int i=x+1;i<=min(n+1,x+k);i++)
        dfs(i,y+a[i]);//i是下一个 
}
dfs(0,0);

记忆化

复制代码
int dfs(int x)
{
    if(x==n+1)return 0;
    dp[x]=INF;
    for(int i=x+1;i<=min(n+1,x+k);i++)
    if(dp[i])dp[x]=min(dp[x],dp[i]+a[i]);
    else
    dp[x]=min(dp[x],dfs(i)+a[i]);
    return dp[x];
}
dfs(0);
复制代码

递推

复制代码
/*dp[n+1]=0;
dp[1]  dp[2] dp[3] .. dp[1+k]*/

for (i=n; i>=0; i--)
{
    dp[i]=INF;
    for (int j=i+1; j<=min(n+1,i+k); j++)
        dp[i]=min(dp[i],dp[j]+a[j]);
}
dp[0]
复制代码

花店橱窗问题

给定一个n*m的矩阵A(n<=m),求一个序列a1,a2,…,an满足1<=a1<a2<…<an<=m。使得A[1][a1]+A[2][a2]+…+A[n][an]最大。A可能有负数。

搜索

复制代码
void dfs(int x,int y,int z)
{
    if(x==n+1)
    {
        if(y==m+1)ans=max(ans,z);
        return;
    }
    for(int i=y+1;i<=m;i++)
    dfs(x+1,i,z+a[x+1][i]);
}
dfs(0,0,0);
复制代码

记忆化搜索

复制代码
int dfs(int x,int y)
{
    if(x==n+1)
    {
        if(y==m+1)return 0;
        else return -INF;
    }
    for(int i=y+1;i<=m;i++)
    {
        if(dp[x+1][i])
        dp[x][y]=max(dp[x][y],dp[x+1][i]+a[x+1][i]);
        else
        dp[x][y]=max(dp[x][y],dfs(x+1,i)+a[x+1][i]);
        return dp[x][y];
    }
}
dfs(0,0);
复制代码

递推

/*dp[n+1][m+1]=0; dp[n+1][0~m]=-INF;
dp[1][]  dp[2][] dp[3][]*/
for (i=n; i>=0;i--)
    for (j=0; j<=m+1; j++)
    for (k=j+1; k<=m+1; k++)
    dp[i][j]=max(dp[i][j],dp[i+1][k]+a[i+1][k]);
cout<<dp[0][0];

 

posted @   Echo宝贝儿  阅读(423)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥
点击右上角即可分享
微信分享提示