dp总结(未完)

news/2025/1/23 12:22:08/文章来源:https://www.cnblogs.com/TobyL/p/18666861

log:

点击查看日志

(25.1.12 开始编写)

.....

(25.1.22 加入完全和多重背包)

(25.1.23 更新例题)


动态规划

对于一个能用动态规划解决的问题,一般采用如下思路解决:
1.将原问题划分为若干 阶段,每个阶段对应若干个子问题,提取这些子问题的特征(称之为 状态);
2.寻找每一个状态的可能 决策,或者说是各状态间的相互转移方式(用数学的语言描述就是 状态转移方程)。
3.按顺序求解每一个阶段的问题。
4.能用dp解决的问题需要有三个要素:最优子结构,无后效性和子问题重叠。

1.背包问题

例1.1 01背包之:[NOIP2005 普及组] 采药

问题:总共有 \(M\) 个草药,每个草药得价值为 \(v\) ,每采一个草药需要花 \(t\) 个时间,要求要在一个固定时间 \(T\) 内使采得的药物总价值最大。

方法1:dfs暴力搜索

解法:一个一个往前摸
完整代码:

#include <bits/stdc++.h>
using namespace std;const int N=1e3+10;
int t[N],v[N],ans;
int T,M;void dfs(int sumv,int pos,int sumt){if(sumt>T) return ;if(pos==M) {ans=max(ans,sumv);return ;}dfs(sumv+v[pos],pos+1,sumt+t[pos]);dfs(sumv,pos+1,sumt);//回溯
}int main(){cin>>T>>M;for(int i=0;i<M;i++){cin>>t[i]>>v[i];}dfs(0,0,0);cout<<ans;return 0;
}

结过:超时,时间复杂度为 \(O(2^N)\) 级别。

方法2:dp(动态规划)

解法:
考虑这几种情况:
1.不摘草药
时间不变,把前面得状态复制过来,

dp[i][j]=dp[i-1][j];

2.采摘草药
当前时间减去该草药所需得时间,并且加上该草药的价值,

dp[i][j]=dp[i-1][j-t[i]]+v[i];

然后进行比较

dp[i][j]=max(dp[i-1][j],dp[i-1][j-t[i]]+v[i]);

完整代码:

#include <bits/stdc++.h>
using namespace std;int t[1001],v[101],dp[1001][1001];  int main(){int T,M;cin>>T>>M;for(int i=1;i<=M;i++){cin>>t[i]>>v[i];} for(int i=1;i<=M;i++){ for(int j=1;j<=T;j++){ if(j>=t[i]){dp[i][j]=max(dp[i-1][j-t[i]]+v[i],dp[i-1][j]);}else{dp[i][j]=dp[i-1][j];}}}cout<<dp[M][T];return 0;
}

结过:AC

例1.2 :[NOIP2001 普及组] 装箱问题

问题:有一个箱子容量为 \(V\),同时有 \(n\) 个物品,每个物品有一个体积。现在从 \(n\) 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。

方法1:dfs 无剪枝暴力

解法:题目就是求背包最多能装多少,只不过是换个问法而已,暴搜挨个试,

void dfs(int pos,int sumw){if(sumw>v) return ;if(pos>n){ans=max(sumw,ans);return ;}dfs(pos+1,sumw+w[pos]);dfs(pos+1,sumw);
}

数据水所以能ac。

方法2:dp(动态规划)

解法:推动态转移方程,有两种情况,装和不装,不装为 \(dp[i][j]=dp[i-1][j]\),装为 \(dp[i][j]=dp[i-1][j]+w[i]\),代码懒得黏了。

1.3空间优化

见此处

1.4 二维01背包

二维01背包就是有两个性质。
题目:NASA的食物计划`
很轻松的推出状态转移方程,几乎和模板没区别,只是多了一维。
1.拿: \(dp[i][j][z]=dp[i-1][j][z]\)
2.不拿:\(dp[i][j][z]=dp[i-1][j-h[i]][z-t[i]]+k[i]\)减去损耗再加上卡路里
得到状态转移方程: \(dp[i][j][z]=max(dp[i-1][j][z],dp[i-1][j-h[i]][z-t[i]]+k[i])\)
代码:

	for(int i=1;i<=n;i++){for(int j=1;j<=h;j++){for(int z=1;z<=t;z++){if(j>=hs[i]&&z>=ts[i]){dp[i][j][z]=max(dp[i-1][j][z],dp[i-1][j-hs[i]][z-ts[i]]+k[i]);}else dp[i][j][z]=dp[i-1][j][z];}	}}

当然还可已进行优化,把之前的滚动数组变成二维:
\(dp[j][z]=max(dp[j][z],dp[j-h[i]][z-t[i]]+k[i])\)

for(int i=1;i<=n;i++){for(int j=h;j>=hs[i];j++){for(int z=t;z>=ts[i];z++){dp[j][z]=max(dp[j][z],dp[j-h[i]][z-t[i]]+k[i])}	}}

1.5 变形题

例:P1509 找啊找啊找GF
多维dp+次要性动态规划,既要保证两个最优,可以设置两个dp来操作,一个人数一个时间,状态转移方程还是很好退的,就怕写的时候漏条件了。
无优化的代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1e2+10;
int n,rmb[N],rp[N],t[N],dpn[N][N][N],dpt[N][N][N];
int main(){int n;cin>>n;for(int i=1;i<=n;i++){cin>>rmb[i]>>rp[i]>>t[i];}int m,r;cin>>m>>r;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){for(int k=1;k<=r;k++){if(rmb[i]<=j&&rp[i]<=k){if(dpn[i-1][j][k]<dpn[i-1][j-rmb[i]][k-rp[i]]+1){dpn[i][j][k]=dpn[i-1][j-rmb[i]][k-rp[i]]+1;dpt[i][j][k]=dpt[i-1][j-rmb[i]][k-rp[i]]+t[i];}else {if(dpn[i-1][j][k]==dpn[i-1][j-rmb[i]][k-rp[i]]+1){dpn[i][j][k]=dpn[i-1][j][k];dpt[i][j][k]=min(dpt[i-1][j][k],dpt[i-1][j-rmb[i]][k-rp[i]]+t[i]);}else {dpn[i][j][k]=dpn[i-1][j][k];dpt[i][j][k]=dpt[i-1][j][k];}}}else {dpn[i][j][k]=dpn[i-1][j][k];dpt[i][j][k]=dpt[i-1][j][k];}}}}cout<<dpt[n][m][r];return 0;
}

进行优化后的代码:

#include <bits/stdc++.h>
using namespace std;
int rmb[1001],rp[1001],t[1001],dpn[1001][1001],dpt[1001][1001];
int main(){int n,m,r;cin>>n;for(int i=1;i<=n;i++) cin>>rmb[i]>>rp[i]>>t[i];cin>>m>>r;for(int i=1;i<=n;i++){for(int j=m;j>=rmb[i];j--){for(int k=r;k>=rp[i];k--){if(dpn[j][k]<dpn[j-rmb[i]][k-rp[i]]+1){dpn[j][k]=dpn[j-rmb[i]][k-rp[i]]+1;dpt[j][k]=dpt[j-rmb[i]][k-rp[i]]+t[i];}else {if(dpn[j][k]==dpn[k-rmb[i]][j-t[i]]+1) dpt[j][k]=min(dpt[j][k],dpt[j-rmb[i]][k-rp[i]]+t[i]);}}}}cout<<dpt[m][r];return 0;
}

状态转移方程
\(dpnum[j][k]=max(dpnum[j][k],dpnum[j-rmb[i]][k-rp[i]]+1)\)
\(dptime[j][k]=min(dptime[j][k],dptime[j-rmb[i]][k-rp[i]]+time[i])\)

1.6 完全背包

不同于01背包,完全背包可以无限次拿取物品,开了

解法:

01背包压维优化可知如果二层循环是正序的话,就会多次拿取物品,符合完全背包的要求。

模板

for(int i=1;i<=n;i++){for(int j=w[i];j<=w;j++){dp[j]=max(dp[j],dp[j-w[i]]+v[i]);}
}

1.7 多重背包

和完全背包不同的是,它拿物品的次数是有限制的,如只能拿 \(num\) 次。

解法和模板:

1.像01背包一样当成单个物品使用。
for(int i=1;i<=n;i++){for(int l=1;l<=num[i];l++){//使用num次for(int j=w;j>=w[i],j--){dp[j]=max(dp[j],dp[j-w[i]]+v[i]);}}
}
for(int i=1;i<=mxx;i++){mx=max(mx,dp[i]);
}
2.考虑使用情况,也就是使用次数。
for(int i=1;i<=n;i++){for(int j=0;j<=w;j++){for(int l=1;l<=num[i];l++){if(l*w[i]<j){dp[j]=max(dp[j],dp[j-l*w[i]]+l*v[i]);}}}
}for(int i=1;i<=mxx;i++){mx=max(mx,dp[i]);
}

1.8 一些例题

1.P6771 [USACO05MAR] Space Elevator 太空电梯

解法:

套多重背包的板子,把价值和重量都变成高度,背包的容积为限制高度,次数为材料可用次数,注意高度限制越低的越往下垒。可得动态转移方程为 \(dp[j]=max(dp[j],dp[j-h[i]]+h[i])\) 。题目整体难度不大,但要注意细节的把控。

代码:
#include <bits/stdc++.h>
using namespace std;
const int N=4e4+10;
int dp[N];
struct Build{int h,a,c;
}b[N];
bool cmp(Build aa,Build b){return aa.a<b.a;
}
int main(){int n,mxx=0;cin>>n;for(int i=1;i<=n;i++){cin>>b[i].h>>b[i].a>>b[i].c;mxx=max(mxx,b[i].a);}sort(b+1,b+n+1,cmp);for(int i=1;i<=n;i++){for(int l=1;l<=b[i].c;l++){for(int j=b[i].a;j>=b[i].h;j--){dp[j]=max(dp[j],dp[j-b[i].h]+b[i].h);}}}int mx=INT_MIN;for(int i=1;i<=mxx;i++){mx=max(mx,dp[i]);}cout<<mx;return 0;
}

2.P5662 [CSP-J2019] 纪念品

解法:

考虑明天剩余的钱,今天买了明天再买,得出动态转移方程 \(dp[k-a[i][j]]=max(dp[k-a[i][j]],dp[k]-a[i][j]+a[i-1][j])\) 。dp[k]相当于还剩 \(k\) 钱的可获得的最大价值,太几把复杂了,当然还有第二种方法。

代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N][N];
int dp[N];
int main(){int t,n,m;cin>>t>>n>>m;int tmp=1;for(int i=1;i<=t;i++){for(int j=1;j<=n;j++){cin>>a[i][j];}}//int mx=0;int ans=m;for(int i=1;i<t;i++){memset(dp,0,sizeof(dp));//初始化dp[ans]=ans;//不买不买时候的价值for(int j=1;j<=n;j++){for(int k=ans;k>=a[i][j];k--){//明天的的钱数dp[k-a[i][j]]=max(dp[k-a[i][j]],dp[k]+a[i+1][j]-a[i][j]);//如果买了,且明天的价值更大就直接卖了}}int mx=0;for(int j=0;j<=ans;j++){mx=max(mx,dp[j]);}ans=mx;}cout<<ans<<endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/873657.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里开源语音克隆CosyVoice2 整合包

CosyVoice2 win整合包 语音克隆CosyVoice2 链接:https://pan.quark.cn/s/5e75615a5cd4 修改webui.py默认值: # 修改默认推理模式 mode_checkbox_group = gr.Radio(choices=inference_mode_list, label=选择推理模式, value=inference_mode_list[1])# 修改随机推理种子 seed =…

KeyClicker 为用户带来真实键盘打字声音体验的应用,再现机械键盘与打字机的打字感受

如果你是一名作家,或者对打字机的声音情有独钟,KeyClicker 将是你的理想选择。许多作家认为,打字机的声音能让他们更专注、更有创作灵感。虽然实体打字机的魅力独特,但它缺乏现代设备的便捷功能,例如高效的编辑与数字化操作。而使用 KeyClicker,你既能享受打字机的经典声…

gdb调试小技巧——多个窗口显示

先用tty显示需要显示的终端的序号 ┌──(root㉿kali)-[~] └─# tty /dev/pts/3然后 vim ~/.gdbinit在文件后加一行 set context-output /dev/pts/2这里数字就是tty显示的数字 设置好之后打开gdb时就可以了

道路流量监测摄像机

道路流量监测摄像机是一种结合了监控摄像技术和交通管理的先进设备,旨在通过实时监测和分析道路上车辆的行驶情况,收集交通流量数据并进行统计分析。这种摄像机在城市交通管理、道路规划、交通安全等领域有着广泛的应用前景。道路流量监测摄像机是一种结合了监控摄像技术和交…

占用消防通道监测摄像机

占用消防通道监测摄像机是一种结合了智能分析技术和监控技术的先进设备,在预防火灾事故和保障人员安全方面具有重要意义。随着社会对安全意识不断提高以及相关法规标准日益完善,相信这种先进设备将会在更多领域得到广泛应用,并为我们创造更加安全、有序的公共环境。占用消防…

安全帽佩戴识别摄像机

安全帽佩戴识别摄像机的应用不仅仅是对现有安全管理模式的一种补充,更是对安全理念的一种革新。它打破了传统安全管理中依靠人工巡检、监督的局限性,实现了安全管理的自动化、智能化。它以一种高效、精准、持续的方式,让安全管理无处不在,让每一个工作人员都时刻处于安全的…

戴头盔识别摄像机

戴头盔识别摄像机是一种结合了智能分析技术和监控技术的先进设备,在提高安全意识和减少事故风险方面具有重要意义。随着社会对安全生产和公共秩序关注度不断提高以及相关法规标准日趋完善,相信这种先进设备将会在更多领域得到广泛应用,并为我们创造更加安全、健康的生活环境…

38个!第六批产业技术基础公共服务平台名单公布

https://mp.weixin.qq.com/s?__biz=MjM5OTUwMTc2OA==&mid=2650924081&idx=1&sn=4b020757ad7a231d0391def504479dbc&chksm=bddf0a2406acfe4e2b9612d57d3de64162d337594a44dc7f061cddcebc4d3a4982a904eb621e&scene=126&sessionid=1737537299#rd

c语言训练程序思维——贪吃蛇未完成版

c语言做的贪吃蛇,未完成,还剩“吃苹果”和“方向”,感兴趣者可以下载玩玩。(初学者,适合训练程序思维,涉及:多维数据、指针(非常值得关注)、结构体……) 通过网盘分享的文件:贪吃蛇源码分享(未完成)链接: https://pan.baidu.com/s/1FciXKqFRG3ZFUMnj7JjtTA 提取码…

plot--绘图库

目录plot--绘图库原文地址简介快速使用 plot--绘图库 原文地址 https://darjun.github.io/2020/04/12/godailylib/plot/简介 本文介绍 Go 语言的一个非常强大、好用的绘图库——plot。plot内置了很多常用的组件,基本满足日常需求。同时,它也提供了定制化的接口,可以实现我们…

查询语句-更新语句

描述查询语句->更新语句的过程:步骤0:编写查询的sql 步骤1:将 “SELECT * FROM” 修改为 “UPDATE” 步骤2:在 “WHERE” 之前新增SET关键字

2025年Rust权威指南、Rust程序设计语言中文版PDF下载

本书英文名为《The Rust Programming Language》,中文版名为《Rust权威指南》,社区翻译版名为《Rust程序设计语言》。 本书由 Rust 核心开发团队编写而成,由浅入深地探讨了 Rust 语言的方方面面。Rust程序员必读入门书籍。2025年《Rust权威指南》、《Rust程序设计语言》中文…