dp刷过的题蓝桥杯前阶段性整理

本文详细讲解了各种类型的背包问题,包括01背包、完全背包、多重背包等,并提供了优化后的代码实现。此外还介绍了子序列相关问题、数字三角形问题、DAG上的动态规划等进阶内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背包问题复习

一、普通01背包

二、完全背包

三、多重背包

四、子序列相关    

五、数字三角形问题&&多段图问题

六、丑数扩展

七、DAG上的dp                                                              

 八、硬币问题

九、01背包变种


蓝桥杯下尽量不要使用min,cin

关于为什么01背包一维必须倒序,感谢我的队友一年不久给我解释清楚了

for(int i=1;i<=n;++i)

  for(int j=1;j<=V;++j)

 {

    dp[j]=min(xxx,dp[j-price[i]]);

}

假如重量是1 2 2 3

那么dp[1]=1(已选用物品1)  dp[2] 可以 在dp[1]的基础上选择物品1组成,即更新过程中,物品1又被利用了,意思是dp[2]是由两个dp[1]组成

注意不要忘记初始化  


一、普通01背包

void solve
{
    for(int i=1;i<=n;++i)
        for(int j=0;j<=W;++j)
        {
            if(k<w[i])
                dp[i][j]=dp[i-1][j];
            else
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
        }

}
优化后
void solve()
{
    for(int i=1;i<=n;++i)
        for(int j=W;j>=w[i];--j)                    倒着的
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}

二、完全背包

void solve()
{
    for(int i=1;i<=n;++i)
        for(int j=0;j<=W;++j)                                            
        {
            if(j<w[i])
                dp[i][j]=dp[i-1][j];
            else
                dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]);            注意对比和01背包的区别
        }
}
优化后
void solve()
{
    for(int i=1;i<=n;++i)
        for(int j=w[i];j<=W;++j)                                        w[i] 到 w
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}


三、多重背包

for(int i=1;i<=n;++i)
    for(int j=W;j>=0;--j)                                                和01背包一致 是倒着的  W 到 0
        for(int k=0;k<=num[i];++k)
            {
                if(j-k*w[i]<0) break;
                dp[j]=max[j-k*w[i]]+k*v[i];
            }

多重部分和问题                                                            多重背包变种     

有n种不同大小的数字a[i],每种有m[i]个,问能否选出他们中的一些刚好组成K                       
for(int i=1;i<=n;++i)
{
    memset(cnt,0,sizeof(cnt));    //cnt记录的是用掉的第i种数的个数
    for(int j=a[i];j<=W;++j)
    {
        if(!dp[j]&&dp[j-a[i]]&&cnt[j-a[i]]<c[i])
        {
            dp[j]=true;
            cnt[j]=cnt[j-a[i]]+1;
        }
    }
}


四、子序列相关    

最大连续子序列的起始和终止 dp[i]以i为结尾的最大连续子序列,当dp[i-1]>0的时候才加上前面
if(dp[i-1]<0) start=i; else dp[i]=dp[i-1]+a[i];
if(dp[i]>ans]) end=i;                                                    就这样

最大上升子序列 dp[i]以i为结尾的最大上升子序列
for i=1:n
{      
    dp[i]=a[i];                                                            勿忘初始化
    for j=1:i-1
        if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+a[i]);
}

五、数字三角形问题&&多段图问题

数塔
要从下面往上算    dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);                   达到层层累加的效果
天上掉馅饼
dp[t][x]代表在时刻t,位置x处可以获得的馅饼数量
for(int i=T-1;i>=1;--i)                                                    层 ------ 序
    for(int j=0;j<11;++j)                                                状态+转移(决策)
        dp[i][j]+=max(dp[i+1][j],dp[i+1][j-1],dp[i+1][j+1]);            从倒数第二层,逐次累加下一层,巧妙的思路
起点体现在最终输出答案dp[0][5];                                            随层次的累加性
多段图也是这个样子,要从最后一列往前加                                        以前一直不明白,为什么第一维遍历是倒序,现在终于想清楚了
现在看看Spy in the metro 也是这个样子,序+状态+状态转转移                     注意在dp过程中,起始位于哪里不重要。最后输出的时候用一下起点即可

dp[i][j] i时刻在j车站的最长还需要等待多少时间

for(int i=终可更新的状态;i>=起;i--)                          //序

      for(从i到j能选择哪几步)                                    //状态

     { 

              处理每步的选择会对结果造成的影响          //决策

     }

 for(int i=1;i<=n-1;++i) dp[T][i]=INF;
        dp[T][n]=0;
        for(int i=T-1;i>=0;--i)
            for(int j=1;j<=n;++j)
            {
                dp[i][j]=dp[i+1][j]+1;
                if(j<n&&HasTrain[i][j][0]&&i+t[j]<=T)
                    dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
                if(j>1&&HasTrain[i][j][1]&&i+t[j-1]<=T)
                    dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
            }
        cout<<"Case Number "<<kase<<": ";
        if(dp[0][1]>=INF) cout<<"impossible\n";                    //事实上结果也有可能dp[0][1]>INF一直在等待
        else
         cout<<dp[0][1]<<endl;


六、丑数扩展

l1,l2,l3分别为p1,p2,p3的指针                                                比priority_queue好用
for(int i=1;i<=idx;++i)
{
    dp[i]=min(dp[l1]*p1,dp[l2]*p2,dp[l3]*p3);
    if(!(dp[i]%p1)) l1++;
    if(!(dp[i]%p2)) l2++;
    if(!(dp[i]%p3)) l3++;
}


七、DAG上的dp                                                              

 求问它和最短路啥关系?

dp[i]=max(dp[j]+v[i]); (i,j)\in E                                         以i为起点的最长路
int dp(i)
{
    int &ans=d[i];
    if(ans>0) return ans;
    ans=v[i];                                                            //它自身的高
    for(int j=0;j<3*n;++j)
    {
        if(G[i][j])
            d[i]=max(ans,dp(j)+v[i]);
    }
    return ans;

}
for(int i=0;i<3*n;++i)
     ans=max(ans,d[i]);

 八、硬币问题

凑成S元最少需要多少硬币和最多需要多少硬币
 最大值:
 int dp(int s)                                                            记忆化搜索
 {
     if(vis[s]) return d[s];
     vis[s]=true;
     int &ans=d[s];
     for(int i=1;i<=n;++i) if(s>v[i]) ans=max(ans,dp(s-v[i])+1);            硬币数最大
     return ans;
 }
for(int i=1;i<=s;++i)                                                    递推法  外层循环是迭代传值,内层循环还是内层循环
    for(int j=1;j<=n;++j)
        if(i>=v[j])
            minv[i]=min(minv[i],minv[i-v[j]]+1);                        maxv相同


电源替换                                                                    还是觉得难以真正的理解
int solve(int u)
{
    if(vis[u]) return dp[u];
    vis[u]=1;
    int &ans=dp[u];
    if(u==0) return 0;
    ans=INF:
    sum=a[u].k;
    for(int i=u;i;--i)
    {
        灯泡u全部替换的价格
        ans=min(ans,sum+solve(i-1));                                    不是ans=min(ans,sum+solve(u-1));
    }
}


九、01背包变种

1、金明的预算方案
for(i=1;i<=m;++i)
        for(j=1;j<=n;++j)
        {
            if(j-w[i][0]>=0)        //主件可以买
            {
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i][0]]+v[i][0]);    //买不买主件
                if(j-w[i][0]-w[i][1]>=0)                                //主件+附件1
                    dp[i][j]=max(dp[i][j],dp[i-1][j-w[i][0]-w[i][1]]+v[i][0]+v[i][1]);
                if(j-w[i][0]-w[i][2]>=0)                                //主件+附件2
                    dp[i][j]=max(dp[i][j],dp[i-1][j-w[i][0]-w[i][2]]+v[i][0]+v[i][2]);
                if(j-w[i][0]-w[i][1]-w[i][2]>=0)                        //主件+附件1+附件2
                    dp[i][j]=max(dp[i][j],dp[i-1][j-w[i][0]-w[i][1]-w[i][2]]+v[i][0]+v[i][1]+v[i][2]);
            }
            else
                dp[i][j]=dp[i-1][j];
        }

特殊的题:求最大值 (ai,bi) 选几对要求和最大,并且sigma A sigma B都不能小于0  思路:考虑前i种的情况下,固定a的值,求b的最大值  amazing~

/dp[i][j]考虑前i对,在a[i]和为j的情况下能得到的最大b值

以及区间由负数的时候把它+区间长度

2、要求被选中的种数最多,在此基础上价值最大
方法:定义dp[j]为时间j最多能刚好唱完多少首歌除了dp[0]=0以外,其他都无法转移过去,求歌曲数目最大值,那么dp[j] j!=0初始化为-INF(保证无法转移过去)
dp[j]=max(dp[j],dp[j-w[i]]+1);
for(int j=t-1;j>=0;--j)  然后倒着找
{
    if(ans<dp[j])
    {
        ans=dp[j];
        t=j+678;
    }
}

3、概率01背包
概率01背包
dp[j]偷j的钱最大的逃跑概率        dp[j]=max(dp[j],dp[j-v[i]]*(1-p[i]));
for(int i=1;i<=n;++i)
    for(int j=W;j>=v[i];--j)
        dp[j]=max(dp[j],dp[j-v[i]]*(1-p[i]));
for(int j=money;j>=0;--j)
{
    if(dp[j]>1-P) ans=j,break;
}
dp[j]花了i钱没学上的概率            dp[j]=min(dp[j],dp[j-v[i]]*(1-p[i]));


最优矩阵链乘+四边形优化
for(int len=2;len<=n;++len)
    for(int i=1;i+len-1<=n;++i)
    {
        int j=i+len-1;
        for(int k=p[i][j-1];k<=p[i+1][j];++k)                        这行总是忘记
        {
            int tmp=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
            if(tmp<dp[i][j])
            {
                dp[i][j]=tmp;
                p[i][j]=k;
            }
        }
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值