动态规划是算法中一门很重要的思想,其通过对每一步的假设规划,不停的寻找最优最有利的解决方案,然后一步一步求解出来。

而01背包是其中最基本的一种dp思想,其题目一般为给定一个容量为V的背包,然后有n件物品,其价值为value[i],每件物品只能最多选择一次或者不选择,问如何才能得到的物品价值最大。

一般dp问题的核心就是一个 状态转移方程 。状态转移方程一出来题目基本上就木得问题了。

通常来说,状态转移方程的代码思想一般如下

for(int i=; i<n; i++)
{
for(int j=; j<=V; j++)
{
if(j>=v[i])
dp[i+][j] = max(dp[i][j], dp[i][j-v[i]]+val[i]);
else
dp[i+][j] = dp[i][j];
}
} 来看看这段代码,将第i件物品放在容量为j的背包中,使得dp[i][j]最大,对于第i件物品来说,有两种选择:
第一,不放这件物品。如果不放这件物品,则dp[i+1][j]=dp[i][j]。
第二,放这件物品。那么首先考虑是否放的下,就是剩下的空间j是否比这件物品的体积v[i]大。
如果大,则在j中腾出v[i]的空间给第i个物品,然后加上其价值与dp[i][j]比较一下就好了,取其中的最大值。
如果j小于v[i],那么这个物品就不要考虑了,直接丢掉就行了。

先看看基础的题目。

1.hdu 2602

这道题目就是一道裸的不能再裸的01背包,给定的容量V,n件物品,每件价值给定,然后只能选一次。

首先学习到了有两种方法解决这道题,第一种是用二维数组,将每次的中间过程都存储下来,状态转移方程又有两种写法。

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

 注意这个是数组从0开始进行for循环

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

 这个是数组从1开始进行for循环

 其中v代表体积,val代表价值

以下是二维数组ac代码:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#define ll long long int
#define inf 1000000007
using namespace std;
int main()
{
int t;
int val[],v[];
int dp[][];
cin>>t;
while(t--)
{
int n,V;
cin>>n>>V;
for(int i=;i<n;i++)
cin>>val[i];
for(int i=;i<n;i++)
cin>>v[i];
for(int i=;i<n;i++)
{
for(int j=;j<=V;j++)
{
if(j>=v[i])
dp[i+][j]=max(dp[i][j],dp[i][j-v[i]]+val[i]);
else
dp[i+][j]=dp[i][j];
}
}
cout<<dp[n][V]<<endl;
}
return ;
}

还有另一种的一维数组的代码,一维数组其实就是对上面二维数组的空间优化,具体好像是对空间的一种优化,对于二维数组来说,中间的每一次循环还记录着,而这些数据都是冗余的,可以不用,所以可以用一维数组进行dp。

状态转移方程如下:

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

为什么要从V开始递减遍历?我举个例子,假设一个物品GG价值1000,体积为2,那么假设我们按【0…..v】这个顺序遍历,那么在j=2时,dp[2] = max(dp[2], dp[0]+1000),那么dp[2] = 1000,当j=4时,dp[4]=max(dp[4], dp[2]+1000), dp[4] = 2000,这时我们再思考一下,GG将被放进背包两次了!如果我们逆序遍历,就可以避免这种结果。

以下是一维数组ac代码:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
using namespace std;
int main()
{
int value[],dp[],v[];
int t;
cin>>t;
while(t--)
{
memset(dp,,sizeof(dp));
memset(value,,sizeof(value));
memset(v,,sizeof(v));
int n,V;
cin>>n>>V;
for(int i=;i<n;i++)
cin>>value[i];
for(int i=;i<n;i++)
cin>>v[i];
for(int i=;i<n;i++) /// i代表的是物体的顺序
{
for(int j=V;j>=v[i];j--) //j代表背包剩余的体积
{
dp[j]=max(dp[j],dp[j-v[i]]+value[i]);
}
}
cout<<dp[V]<<endl;
}
return ;
}

2.hdu 2546

Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
 

Input

多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。

 
Output
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
 

Sample Input

1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0
 

Sample Output

-45 32

题意大概就是饭卡的钱如果大于5,就能用,小于5就不能刷了,然后可以透支,如果大于5则可以减去一个数得到负值,问如何买使得剩下的钱最少。

题目思路一般就是先用sort把物品价值排个序,然后把最大的记录下来,然后用dp来使得剩余的钱尽可能的少并且大于5元。

ac代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
int main()
{
int val[],dp[];
int n,m;
while(cin>>n&&n)
{
memset(dp,,sizeof(dp));
memset(val,,sizeof(val));
for(int i=;i<n;i++)
cin>>val[i];
sort(val,val+n);
int maxn=val[n-];
cin>>m;
if(m<) cout<<m<<endl;
else
{
for(int i=;i<n-;i++) //这里要少一个,因为最大的已经记录下来了
for(int j=m-;j>=val[i];j--)
{
dp[j]=max(dp[j],dp[j-val[i]]+val[i]);
}
cout<<m-maxn-dp[m-]<<endl;
} }
return ;
}

DP学习之路(1) 01背包的更多相关文章

  1. poj 2184(dp变形,进一步加深01背包)

    点击打开链接 题意: 给你n个物品,每个物品都有两个属性,s和f,要求选择一些物品,使sum(s)+sum(f)最大,并且sum(s)>=0&&sum(f)>=0, 根据0 ...

  2. Re0:DP学习之路 Proud Merchants HDU - 3466

    解法 排序+01背包 这里的排序规则用q-p升序排列这里是一个感觉是一个贪心的策略,为什么这样做目前也无法有效的证明或者说出来 然后就是01背包加了一个体积必须大于什么值可以装那么加一个max(p,q ...

  3. Re0:DP学习之路 饭卡 HDU - 2546

    解法 01背包变式,首先贪心的想一下如果要保证余额最小那么就需要用相减后最小的钱减去之前最大的价格,且得保证这个钱在5元以上 对于寻找如何减最多能包含在5元以上,这里用01背包 我们把价钱看做体积装进 ...

  4. Educational DP Contest E - Knapsack 2 (01背包进阶版)

    题意:有\(n\)个物品,第\(i\)个物品价值\(v_{i}\),体积为\(w_{i}\),你有容量为\(W\)的背包,求能放物品的最大价值. 题解:经典01背包,但是物品的最大体积给到了\(10^ ...

  5. Re0:DP学习之路 01背包如何打印路径?

    伪代码 用二维数组记录,如果出现可以转移的dp那么记录bk[当前体积][装的物品]=1 输出的时候倒推,如果存在连通的边那么输出并且总共的体积减去输出的体积 代码(uva-624,目前wa不明所以,网 ...

  6. Re0:DP学习之路 母牛的故事 HDU - 2018

    解法 一定要注意斐波那契数列的原始意义,斐波那契数列也叫作兔子数列是兔子繁衍的一种表示方法.同样适用于别的情况的动物繁衍问题 原始的是3个月一胎现在四个月那么方程就是 f(n)=n n<=4 f ...

  7. Re0:DP学习之路 数塔 HDU - 2084(基础递推)

    解法 首先是输入的问题,输入的时候还要注意每一层都有多少个 然后是怎么求解,一般求解首先要考虑顺序,是正序还是倒序 如果这个题是正序的话那么最终还需要将最后一行进行一次找max的运算 如果是倒序的话那 ...

  8. Webwork 学习之路【01】Webwork与 Struct 的前世今生

    Struts 1是全世界第一个发布的MVC框架,它由Craig McClanahan在2001年发布,该框架一经推出,就得到了世界上Java Web开发者的拥护,经过长达6年时间的锤炼,Struts ...

  9. hdu 2955 01背包

    http://acm.hdu.edu.cn/showproblem.php?pid=2955 如果认为:1-P是背包的容量,n是物品的个数,sum是所有物品的总价值,条件就是装入背包的物品的体积和不能 ...

随机推荐

  1. OpenLayers的view与layer:控制显示内容

    view与layer都可以进行显示内容的控制.这两者负责的功能是由区别的. view即显示的地图容器,有以下几个属性: center:[经度,纬度] ,对应的设置函数为view.setCenter() ...

  2. windows 映射samba Linux服务器,输入正确的账号密码却提示“ 指定的网络密码不正确

    重启Linux samba服务也没用,重启Linux和windows系统也没用,急!!! 最佳答案 linux中要添加对应的系统用户和samba用户useradd titiansmbpasswd -a ...

  3. MDK,关于 STM32F4 配置失败, GPIO, USART 写入值没反应

    需要先将RCC->AHB1ENR寄存器的对应时钟打开! 下面做个测试: 配置GPIO实验 没有打开时钟使能,配置无反应: 打开时钟使能后,可以成功写入数据: 配置USART实验 RCC 未开启时 ...

  4. [kuangbin带你飞]专题一 简单搜索 - E - Find The Multiple

    //Memory Time //2236K 32MS #include<iostream> using namespace std; ]; //保存每次mod n的余数 //由于198的余 ...

  5. selenium基础(窗口截图)

    窗口截图 目的:当脚本执行出错时对当前窗口进行截图 方法:get_screenshot_as_file() #打开百度首页,搜索“selenium",完成后进行截图,并将结果保存至D:/te ...

  6. js 验证图片

    var selectedImg = e.target.files[0]; //获取图片 var isPic = /^(image\/bmp|image\/gif|image\/jpeg|image\/ ...

  7. 深入浅出 Java Concurrency (36): 线程池 part 9 并发操作异常体系[转]

    并发包引入的工具类很多方法都会抛出一定的异常,这些异常描述了任务在线程池中执行时发生的例外情况,而通常这些例外需要应用程序进行捕捉和处理. 例如在Future接口中有如下一个API: java.uti ...

  8. C++中无数据成员的类的对象占用内存大小

    结论: 对于没有数据成员的对象,其内存单元也不是0,c++用一个内存单元来表示这个实例对象的存在. 如果有了数据或虚函数(虚析构函数),则相应的内存替代1标记自己的存在. PS:以下代码均在win32 ...

  9. FreeMarker 自定义 TemplateDirectiveModel(二)

    FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出.FreeMarker 与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP.它不 ...

  10. LocalSessionFactoryBean有几个属性查找hibernate映射文件

    LocalSessionFactoryBean有几个属性查找hibernate映射文件: mappingResources.mappingLocations.mappingDirectoryLocat ...