初学DP

来源:互联网 发布:什么是网络在线客服 编辑:程序博客网 时间:2024/06/13 13:19

最开始遇到的第一个问题那就是最经典的 0 1 背包问题

上来先是用递归的暴力搜索引出来的,但是看了书上的代码,我觉得有必要重新看一下搜索的问题了

题目大意:从n个物品中选出总重量不超过m的最大价值

输出 n = 4,m = 5

(w,v) = {{2,3},{1,2},{3,4},{2,2}};

输出 7

#include <iostream>#define max(x,y) ((x)>(y)?(x):(y))using namespace std;int w[100];int v[100];int n,m;int rec(int i,int j)//搜索函数 ,i 表示当前是第几个背包,j表示不重量大于j的最大价值 {int res;//定义一个 返回值 if(i==n)//如果到最后一个,返回价值0 res = 0;else if(j<w[i])// 如果当前重量大于j 跳过当前 res = rec(i+1,j);else res = max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);//这就是关键一步,取二者的较大值,如果选取了当前物品的话,就要在递归完成后出的来的时候加上当前物品的价值,我说的你肯定也不好理解,还是多看多敲代码 return res;}/*  还有更简洁的写法,也是好多nb代码的样子那样,我现在觉得写成那样的不一定就是大牛,它也许就是想装B,让你看不懂int rec(int i,int j){if(i==n)return 0;else if(j<w[i])return rec(i+1,j);else return max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);} 这样是不是很牛X的样子呢??其实都j8一个道理,就是tm看着帅, */ int main(){cin>>n>>m;//n 表示有多少个物品,m表示最大体积,我并不是在做题,所以并没有写多组测试数据 for(int i=0;i<n;i++){cin>>w[i]>>v[i];}cout<<rec(0,m)<<endl;} 

但是这种暴力递归效率很低,只要没到达出口,它就会一直递归下去,而且不管是否重复过(递归重复这个概念很容易理解,就比如在整数划分中 3 1 1 和1 3 1 是同一种情况),而DP它唯一的不同就是多用了一个DP数组来记录每一步运算的结果,并在每一次的循环之前先判断一下是否重复计算,如果重复了,直接把前面的值给取出来用就好了,说了这么一大堆废话,其实道理很简单,可就是想不到啊。。。。

下面是上面的升级版本---就是传说中的dp了

#include <iostream>#include <cstring>#define max(x,y) ((x)>(y)?(x):(y))using namespace std;int w[100];int v[100];int dp[110][110]; int n,m;int rec(int i,int j){if(dp[i][j]>=0)//如果这个值我们已经计算过了,那就直接那来用 return dp[i][j];int res;if(i==n)//到达结尾,返回0; res = 0;else if(j<w[i])//不满足重量,跳过 res = rec(i+1,j);else res = max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);//取较大值dp[i][j] = res;//这里区别上面的就是我们每次是记录结果,而不是返回结果 }/*  ZB写法 int rec(int i,int j){if(dp[i][j]>=0)return dp[i][j];if(i==n)dp[i][j] = 0;else if(j<w[i])dp[i][j] = rec(i+1,j);else dp[i][j] = max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);}*/int main(){cin>>n>>m;memset(dp,-1,sizeof(dp));for(int i=0;i<n;i++){cin>>w[i]>>v[i];}rec(0,m);/*for(int i=0;i<n;i++)//这个循环可以输出我们记录的所以情况对理解很有帮助 {for(int j=0;j<=m;j++){cout<<"dp["<<i<<"]"<<"["<<j<<"]"<<"="<<dp[i][j]<<"  ";}cout<<endl;}*/cout<<dp[0][m]<<endl;//这里输出对应的数组位置 return 0;} 

接下来就是非递归的写法了

#include <iostream>#define max(x,y) ((x)>(y)?(x):(y))using namespace std;int dp[110][110];int w[110],v[110];int main(){int m,n;while(cin>>n>>m){int i,j;for(i=0;i<n;i++)cin>>w[i]>>v[i];for(i=0;i<=n;i++){for(j=0;j<=m;j++){if(j<w[i])//如果当前 j 小于 w[i] 就跳过当前物品 dp[i+1][j] = dp[i][j];else dp[i+1][j] = max(dp[i][j],dp[i][j-w[i]]+v[i]);//下一个的值等于 跳过上一个或者选取上一个中的最大值 }}cout<<dp[n][m]<<endl;}return 0;} 

讲道理还是有点似懂非懂,太菜怪我咯。。。

0 0
原创粉丝点击