专题复习--背包问题+例题(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 ...
随机推荐
- docker-lnmp 多容器部署 Laravel 方案分享(转)
docker lnmp 多容器部署方案.完全基于 docker 官方镜像,遵循最佳实践,一容器一进程. github 项目地址 https://github.com/March7/docker-lnm ...
- Arm-Linux 移植 Ubuntu
前言: 这篇文章介绍如何构建一个完整基于ARM的Ubuntu系统. 由于改文章当初写的时候是发表在国外产品论坛上面,故保留了原文内容. 使用到的硬件平台:Geekbox 补充说明: 虽然Geekbox ...
- Ayoub's function
思维,就是反过来想,题解太强了 #include <bits/stdc++.h> using namespace std; int main() { long long t; cin> ...
- mongodb - 关联字段
1,博客表结构 Blog.js var mongoose = require('mongoose') mongoose.connect('mongodb://localhost/test',{ us ...
- Java提升四:Stream流
1.Stream流的定义 Stream是Java中的一个接口.它的作用类似于迭代器,但其功能比迭代器强大,主要用于对数组和集合的操作. Stream中的流式思想:每一步只操作,不存储. 2.Strea ...
- junit小试log4j及xml配置文件说明
上篇文章介绍了java日志框架使用情况,以及xml配置文件的简单说明.但主要还是根据别人的博客整理一下知识结构,只能是纸上谈兵,本文通过junit测试框架来玩玩log4j. 1.junit+l ...
- 初学者的困惑:OOP与一般编程的区别
*在写<程序猿的思维修炼>随笔中,我们大概猜想到了,OOP的思想更趋于模块化,更独立,因此称为一个个对象,本次随笔将对OOP和一般编程的区别有更详细的解释 面向对象编程的含义: 面向对象编 ...
- js实现连续输入之后发送请求
输入框是我们经常会用到的功能,想要实现输入就请求的功能 但是在实际开发中,为了减少服务器压力,会在输入之后停留1s没有输入之后再进行搜索 研究之后用原生js及表单写了一个简单的demo,如果有好的de ...
- Windows 10中使用VirtualBox
新版Windows 10或者安装了新的更新以后,VirtualBox虚拟机就不能用了. 原因是WIndows10里面有个叫Virtualization-base security的安全机制打开了. 关 ...
- Bug 佛祖镇楼
原文链接:https://www.cnblogs.com/xdp-gacl/p/4198935.html // _ooOoo_ // o8888888o // 88" . "88 ...