dp动态规划 背包问题
01背包(每个物品就1个)
理解:
动态转移方程:
dp[i][j]=max(dp[i-1][j] , dp[i-1][ j-p[i] ]+m[i]);
代码:
#include <iostream> #include<cstring> #include<stdlib.h> #include<math.h> #include<algorithm> using namespace std; int main() { int n,x; while(~scanf("%d %d",&n,&x)) { int p[n+1],m[n+1]; int i,j; int dp[n+1][x+1]={{0}}; for(i=1;i<=n;i++) { cin>>p[i]>>m[i]; } for(i=0;i<=x;i++) { dp[0][i]=0; } for(i=1;i<=n;i++) { for(j=1;j<=x;j++)//j表示当前背包容量 { if(p[i]> j)//如果该物品容量大于当前背包容量 { dp[i][j]=dp[i-1][j]; } else { dp[i][j]=max(dp[i-1][j] , dp[i-1][ j-p[i] ]+m[i]); } } } cout<<dp[n][x]<<endl; } return 0; }
改进
因为每层都只于上层有关,故压缩数组,
又因为从小到大会覆盖到上层数据,故从小到大
更新数据
优化后:
#include <iostream> #include<cstring> #include<stdlib.h> #include<math.h> #include<algorithm> using namespace std; int main() { int n,x; while(~scanf("%d %d",&n,&x)) { int p[n+1],m[n+1]; int i,j; int dp[x+1]= {0}; for(i=1; i<=n; i++) { cin>>p[i]>>m[i]; } for(i=0; i<=x; i++) { dp[i]=0; } for(i=1; i<=n; i++) { for(j=x; j>=1; j--) //j表示当前背包容量 { if(j>=p[i]) { dp[j]=max(dp[j], dp[j-p[i] ]+m[i]); } } } cout<<dp[x]<<endl; } return 0; }
完全背包问题(物品个数无限)
朴素算法
只比01背包多了一层更新放置几个物品的循环
#include <iostream> #include<cstring> #include<stdlib.h> #include<math.h> #include<algorithm> using namespace std; int main() { int n,x; while(~scanf("%d %d",&n,&x)) { int p[n+1],m[n+1]; int i,j; int dp[x+1]= {0}; for(i=1; i<=n; i++) { cin>>p[i]>>m[i]; } for(i=0; i<=x; i++) { dp[i]=0; } for(i=1; i<=n; i++) { for(j=x; j>=1; j--) //j表示当前背包容量 { //*****************只多这些 for(int k=0;k<=j/p[i];k++) //只多这些***************** dp[j]=max(dp[j],dp[ j- k*p[i] ]+k*m[i]); } } cout<<dp[x]<<endl; } return 0; }
改进中间态
动态转移方程:
dp[i][j]=max(dp[i-1][j] , dp[ i ] [ j-p[i] ]+m[i]);
与01区别:后面为dp[i]行
改进
由改进中间态得:只需要把01改进算法,改为从小到大推即可
#include <iostream> #include<cstring> #include<stdlib.h> #include<math.h> #include<algorithm> using namespace std; int main() { int n,x; while(~scanf("%d %d",&n,&x)) { int p[n+1],m[n+1]; int i,j; int dp[x+1]= {0}; for(i=1; i<=n; i++) { cin>>p[i]>>m[i]; } for(i=0; i<=x; i++) { dp[i]=0; } for(i=1; i<=n; i++) { for(j=p[i]; j<=m; j++)//从可以放入一个p[i]开始处理 //j表示当前背包容量 { dp[j]=max(dp[j],dp[ j- p[i] ]+m[i]); } } cout<<dp[x]<<endl; } return 0; }
多重背包(物品个数有限)
两个条件:
1.和完全背包一样
2.数量个数有限
朴素算法
代码思想:从01背包的状态转移方程式,去增加第i个物品拿k个的循环
#include <iostream> #include<cstring> #include<stdlib.h> #include<math.h> #include<algorithm> using namespace std; int main() { int p[501];//价格 int v[501];//价值 int s[501];//数量 int dp[6100]; int n,m; cin>>n>>m; int i,j,k; for(i=1;i<=n;i++) { cin>>p[i]>>v[i]>>s[i]; } for(i=1;i<=n;i++) { for(j=m;j>=1;j--) { for(k=0;k<=s[i]&&j>=k*p[i];k++) { //从01背包的状态转移方程式,去增加第i个物品拿k个的循环 dp[j]= max(dp[j],dp[j-k*p[i]] +k*v[i] ); } } } cout<<dp[n]; return 0; }
改进:利用二进制
没学过二进制, 就不写了
二进制思想:
假设有 1000 个苹果,现在要取n个苹果,如何取?朴素的做法应该是将苹果一个一个拿出来,直到n个苹果被取出来。
再假设有 1000 个苹果和10只箱子,利用箱子进行某些预工作,然后如何快速的取出n个苹果呢?So..可以在每个箱子中放 2^i (i<=0<=n)个苹果,也就是 1、2、4、8、16、32、64、128、256、489(n减完之前的数之后不足 2^i,取最后剩余的数),相当于把十进制的数用二进制来表示,取任意n个苹果时,只要推出几只箱子就可以了。
基于这种思想把一种多件物品转换为,多件一种物品,然后用01背包求解即可。
二维费用背包
解法:多加一维就可
const int maxn=1e4+5; int n,x,y,w[N+1],v[N+1],g[N+1]; int dp[X+1][Y+1]; int main() { cin>>n>>x>>y; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>g[i]; for(int i=1;i<=n;i++) { for(int j=x;j>=w[i];j--) { for(int k=y;k>=v[i];k--) { dp[j][k]=max(dp[j][k],dp[j-w[i]][k-v[i]]+g[i]); } } } cout<<dp[x][y]<<endl; return 0; }
实战1.sdut- 最少硬币问题
#include <iostream> #include <cstdio> #include <cstring>///包含memset() #define MAX 0x3f3f3f3f ///定义最大值MAX using namespace std; int min(int, int); int T[15]; //T[i]表示 第i种硬币的面值 int coins[15]; //coins[i]表示第i种硬币的个数 long long int dp[20011]; //dp[目标面额] 表示达到当前面额需要最少的硬币数量 int main() { int n, m; scanf("%d", &n); for(int i = 0; i < n; i++) cin>>T[i]>>coins[i]; scanf("%d", &m); memset(dp, MAX, sizeof(dp));//初始化为最大值 dp[0] = 0;//边界条件,当目标面额是0时,需要0个硬币 for(int i = 0; i < n; i++) {//i是第i种硬币,遍历每一种硬币 for(int j = 1; j <= coins[i]; j++) {//j是硬币个数,每一种硬币有coins[i]个,对第i种硬币的每一个硬币都遍历 for(int k = m; k >= T[i]; k--) {//k是当前面额,当需要找回的面额 大于等于 第i种硬币的面值 //判断已保存的dp[当前面额]的值 和 dp[替换掉当前面额]的值 的大小 dp[k] = min(dp[k], dp[k - T[i]] + 1); } } } printf("%lld\n", dp[m] < MAX ? dp[m] :-1); return 0; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/15151376.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步