专题复习--背包问题+例题(HDU 2602 、POJ 2063、 POJ 1787、 UVA 674 、UVA 147)
背包问题
应用场景
给定 $n$ 种物品和一个背包。物品 $i$ 的重量是 $w_i$ ,其价值为 $v_i$ ,背包的容量为C。应该如何选择装入背包中的物品,使得装入背包的总价值最大?
*01 背包
*01 背包特点:
给定 $n$ 种物品和一个背包 ( 每个物品只能选取一个)。物品$i$ 的重量是$w[i]$,其价值为$v[i]$,背包的容量为C。应该如何选择装入背包中的物品,使得装入背包中的物品的总价值最大?
*状态:
$dp[i][j]$ 表示在只能从 $1-i$ 个物品中选择物品并且背包容量大小为 $j$ 的情况下,所能获得的最大价值。
*限制条件
• 只能选择物品 1 ~ i
• 选择物品的总重量不能超过 $j$
*状态转移方程:
$dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])$;
*实现:
1)初始化:
int n,m; //n表示物品个数,m表示背包容量
for (int i = ;i<= m ;i++)
dp[][i] = ;
2)-1二维数组实现:
for (int i = ;i <= n ;i++){
for (int j = ;j<=m ;j++){
dp[i][j] = dp[i-][j];
if (j-w[i]>=)
dp[i][j] = max(dp[i][j],dp[i-][j-w[i]]+v[i]);
}
}
2)-2一维数组实现
for (int i = ;i <= n ;i++){
for (int j = m ;j>= ;j--){
if (j-w[i]>=)
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}
}
*完全背包
*完全背包特点
给定 $n$ 种物品和一个背包 ( 每个物品选取无限个)。物品 $i$ 的重量是 $w[i]$,其价值为$v[i]$,背包的容量为$C$。应该如何选择装入背包的物品,是的装入背包中物品的总价值最大?
*状态
$dp[i][j]$ 表示在只能从 $1-i$ 个物品中选择物品并且背包容量大小为 $j$ 的情况下,所能获得的最大价值。
*限制条件
• 只能选择物品 1 ~ i
• 选择物品的总重量不能超过 j
*状态转移方程
$dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i])$;
*实现
1)初始化
int n,m; //n表示物品个数,m表示背包容量
for (int i = ;i<= m ;i++)
dp[][i] = ;
2)-1二维数组实现
for (int i =;i <= n;i++){
for (int j = ;j <= m;j++){
dp[i][j]=dp[i-][j];
if (w[i]<=j)
dp[i][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
}
}
2)-2一维数组实现
for (int i =;i <= n;i++){
for (int j = ;j <= m;j++){
if (d-w[i]>=)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
*多重背包
*多重背包特点
给定 $n$ 种物品和一个背包 ( 每个物品选取有限个)。物品 $i$ 的重量是 $w[i]$ ,其价值为$v[i]$,每个物品可以取$k[i]$个,背包的容量为C。应该如何选择装入背包中的物品使得装入背包中的物品总价值最大?
*状态
$dp[i][j]$ 表示在只能从 $1-i$ 个物品中选择物品并且背包容量大小为 $j$ 的情况下,所能获得的最大价值
*限制条件
• 只能选择物品 1 ~ i
• 选择物品的总重量不能超过 j
*状态转移
$dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i], dp[i-1][j-w[i]*2] + v[i]*2,...)$
*实现
1)初始化
2)-1二维数组实现
for (int i = ;i <= n ;i++){
for (int j = ;j<=m ;j++){
dp[i][j] = dp[i-][j];
for (int z = ; z<=k[i]; z++)
if (j-w[i]*z>=)
dp[i][j] = max(dp[i][j],dp[i-][j-z*w[i]]+z*v[i]);
else
break;
}
}
2)-2一维数组实现
for (int i = ;i <= n ;i++){
for (int j = m ;j>= ;j--){
for (int z = ; z<=k[i] ;z++)
if (j-w[i]*z>=)
dp[j] = max(dp[j],dp[j-z*w[i]]+z*v[i]);
else
break;
}
}
*多重背包问题转化为 01 背包问题
1. p 以下的数字可否由若干不同数字通过组合得到
(a) $2^n$- 1 $n$ == 7 二进制: 111111 000001、000010、000100、001000、010000、100000
(b) $k$ + $2^n$ -1 < $2^{n+1}$ -1 $n$ == 7
• $2^n$ -1 及以下的数字可以由以下数字表示 000001、000010、000100、001000、010000、100000
• 大于 $2^n$ -1 的数字 $k$ 加上以上 7 个数字组成的数字可以表示任何大于$2^n$ - 1小于等于k+$2^n$-1的数字
2. 代码实现
//num[i] 表示第i种硬币可以取多少次
//w[j] 表示转后为01背包后 只能取一次的硬币的价值与重量
int totlW = ;
memset(w,,sizeof w);
for (int i = ;i< ; i ++){
for(int j=; j<=num[i]; j<<=){
w[totlW++]=j*(i+);
num[i]-=j;
}
if(num[i]>)
w[totlW++]=num[i]*(i+);
}
背包问题
HDU 2602
POJ 2063
POJ 1787
UVA 674
UVA 147
第一题是01背包的板子题,就直接放代码了:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
int t;
int n,m;
int val[],w[],f[];
int main ()
{
scanf ("%d",&t);
while (t--)
{
scanf ("%d%d",&n,&m);
memset(f,,sizeof(f));
for (int i = ;i <= n;i++)
scanf ("%d",&val[i]);
for (int i = ;i <= n;i++)
scanf ("%d",&w[i]);
for (int i = ;i <= n;i++)
for (int j = m;j >= w[i];j--)
f[j]=max(f[j],f[j-w[i]]+val[i]);
printf ("%d\n",f[m]);
}
return ;
}
第二道题是完全背包,因为每个债券都是1000的倍数,所以我们可以直接把总钱数和债券的数额都直接除以1000.因为每年都会有利息,所以每年的总钱数会增加,求出每一年的最大利益,总钱数加上最大利益就是下一年的总钱数。所以,我们只需要对于每一年的总钱数求一个完全背包,每年更新总钱数就好。代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int N = ;
int dp[N];
int num[N], val[N];
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int money,year,d;
scanf("%d %d", &money, &year);
scanf("%d", &d);
for(int i=;i<d;i++)
{
scanf("%d %d", &num[i], &val[i]);
num[i]/=;
}
int sum=money;
for(int i=;i<year;i++)
{
memset(dp,,sizeof(dp));
money=sum;
money/=;
for(int j=;j<d;j++)
{
for(int k=num[j];k<=money;k++)
{
dp[k]=max(dp[k-num[j]]+val[j],dp[k]);
}
}
sum+=dp[money];
}
printf("%d\n",sum);
}
return ;
}
第三题是完全背包+路径输出,$dp[j]$表示总价格为$j$时最多可以有多少个硬币,但是需要维护一下硬币的个数,实际上在01背包中,我们对所有状态都更新了一次答案,这里我们等于是对4种硬币各跑一次背包,维护一下使用次数即可。$used[j]$表示总价格为j时用了多少个某个硬币。$pre[j]$记录方案,表示j出现更有的答案时是由哪个状态转移来的。最后遍历一下输出答案就好了。
#include <stdio.h>
#include <string.h>
using namespace std;
int dp[], used[], pre[], coin[] = {, , , }, num[], ans[];
int main(){
int p;
while(scanf("%d %d %d %d %d", &p, &num[], &num[], &num[], &num[]) != EOF){
if(p + num[] + num[] + num[] + num[] == ) return ;
for(int i = ; i <= p; ++i){
dp[i] = -1e9;
}
memset(pre, , sizeof(pre));
pre[] = -;
dp[] = ;
for(int i = ; i < ; ++i){
memset(used, , sizeof(used));
for(int j = coin[i]; j <= p; ++j){
if(dp[j] < dp[j - coin[i]] + && used[j - coin[i]] < num[i]){
used[j] = used[j - coin[i]] + ;
dp[j] = dp[j - coin[i]] + ;
pre[j] = j - coin[i];
}
}
}
if(dp[p] <= ){
printf("Charlie cannot buy coffee.\n");
continue;
}
memset(ans, , sizeof(ans));
while(){
if(pre[p] == -) break;
ans[p - pre[p]]++;
p = pre[p];
}
printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", ans[], ans[], ans[], ans[]);
}
}
第四题一个简单的动态规划,比如要求55的组成方案数,必须要知道55-1=54的方案数 和 55-5=50的方案数,和50-10=45的方案数和55-25=30的方案数和55-50=5的方案数,所以dp以此类推,每次更新dp。
#include <cstring>
#include <iostream>
#include <cstdio>
using namespace std;
int f[];
int main ()
{
int v;
while(cin>>v){
memset(f,,sizeof(f));
int c[]={,,,,,};
f[]=;
for(int i = ; i <= ; ++i)
for(int j = c[i]; j <= v; ++j)
{
f[j] = f[j]+f[j-c[i]];
}
cout << f[v]<<endl;
}
return ;
}
专题复习--背包问题+例题(HDU 2602 、POJ 2063、 POJ 1787、 UVA 674 、UVA 147)的更多相关文章
- HDU 2602 Bone Collector (01背包问题)
原题代号:HDU 2602 原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602 原题描述: Problem Description Many yea ...
- HDU 2602 Bone Collector 0/1背包
题目链接:pid=2602">HDU 2602 Bone Collector Bone Collector Time Limit: 2000/1000 MS (Java/Others) ...
- POJ 2063 Investment (完全背包)
A - Investment Time Limit:1000MS Memory Limit:30000KB 64bit IO Format:%I64d & %I64u Subm ...
- poj 2063 Investment ( zoj 2224 Investment ) 完全背包
传送门: POJ:http://poj.org/problem?id=2063 ZOJ:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problem ...
- POJ 2289 Jamie's Contact Groups / UVA 1345 Jamie's Contact Groups / ZOJ 2399 Jamie's Contact Groups / HDU 1699 Jamie's Contact Groups / SCU 1996 Jamie's Contact Groups (二分,二分图匹配)
POJ 2289 Jamie's Contact Groups / UVA 1345 Jamie's Contact Groups / ZOJ 2399 Jamie's Contact Groups ...
- HDOJ(HDU).2602 Bone Collector (DP 01背包)
HDOJ(HDU).2602 Bone Collector (DP 01背包) 题意分析 01背包的裸题 #include <iostream> #include <cstdio&g ...
- 倍增&矩阵乘法 专题复习
倍增&矩阵乘法 专题复习 PreWords 这两个基础算法我就不多说啦,但是还是要介绍一下" 广义矩阵 "乘法 其实就是把矩阵换成取\(max\),然后都一样... 据神仙 ...
- 状压dp专题复习
状压dp专题复习 (有些题过于水,我直接跳了) 技巧总结 : 1.矩阵状压上一行的选择情况 \(n * 2^n\) D [BZOJ2734][HNOI2012]集合选数 蒻得不行的我觉得这是一道比较难 ...
- POJ 1496 POJ 1850 组合计数
Code Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 8256 Accepted: 3906 Description Tran ...
随机推荐
- asp.net+bootstrap上传图片+FileUpload控件文件上传下载
ps:我数据库使用的pgsql,看个人修改. 代码asp.net 的,使用了mootools框架,里面包含了bootstrap上传图片,查看预览,还加了个上传任意文件的FileUpload.(界面随便 ...
- Mysql使用存储过程创建测试数据
一.概述 存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集.其存储在数据库中,经过第一次编译后调用不需要再次编译,用户通过指定存储过程的名字并给出 ...
- NO25 三剑客之SED行天下
功能说明 Sed是Stream Editor(流编辑器)缩写,是操作.过滤和转换文本内容的强大工具.常用功能有增删改查,过滤,取行. [root@oldboy ~]# sed --version #→ ...
- Bar Chart _Study
---恢复内容开始--- 以“3D BarChart”为例. 1.Select a theme.(选择一个主题模板) 2.Set up categories and groups.(设置类型和组) 3 ...
- 2-10 就业课(2.0)-oozie:13、14、clouderaManager的服务搭建
3.clouderaManager安装资源下载 第一步:下载安装资源并上传到服务器 我们这里安装CM5.14.0这个版本,需要下载以下这些资源,一共是四个文件即可 下载cm5的压缩包 下载地址:htt ...
- 2-10 就业课(2.0)-oozie:3、安装2
第七步:修改oozie-site.xml cd /export/servers/oozie-4.1.0-cdh5.14.0/conf vim oozie-site.xml 如果没有这些属性,直接添加进 ...
- 数据库-----catalog与schema简介
在SQL环境下Catalog和Schema都属于抽象概念,主要用来解决命名冲突问题 一个数据库系统包含多个Catalog,每个Catalog包含多个Schema,每个Schema包含多个数据库对象(表 ...
- edge浏览器两个标签页localStorage不同步,解决办法
今天遇到个奇怪的问题,edge两个标签页之间的localStorage值不同步,网上说ie和edge如果想让localStorage值同步,需要主动出发localStorage的change事件 wi ...
- JS - 获取任意一天的0点和23:59:59时间
转载自 https://www.cnblogs.com/sk-3/archive/2019/07/23/11232750.html 使用了setHours() 方法 setHours() 方法用于设置 ...
- sql编程利器,Sql Prompt下载及安装方法
Sql Prompt只能提示及其格式化用起来非常方便: 推荐网址:www.4-yecao.com 免费下载地址:http://download.csdn.net/detail/caizz520/455 ...