hdu 2159FATE(完全背包)
https://www.cnblogs.com/violet-acmer/p/9852294.html
题解:
思路一:完全背包转“01”背包
考虑到第ki个怪最多杀min(m/b[ki],s)个,于是可以把第ki个怪转化为min(m/b[ki],s)个忍耐度及经验值均不变的怪,然后求解这个01背包问题。
(1):不用滚动数组优化
本题有三个限制条件①怪物种类②忍耐度③杀怪数。
如果不使用滚动数组优化空间,则需要开个三维数组dp[ maxMaster ][ max_m ][ max_s ]。
dp[ tot ][ i ][ j ]的含义是杀第tot个怪时,耗费 i 个忍耐度和 j 个杀怪数所获得的最大经验值。
void Solve()
{
int tot=;//把所有的 ki 怪转化为min(s,m/b[ki])个忍耐度及经验值均不变的物品时的总个数
for(int kind=;kind <= k;++kind)
{
int x=min(s,m/b[kind]);//第 ki 个怪最多可转化成 x 个
while(x--)//将这 x 依次加入到背包中
{
for(int i=;i <= m;++i)//当前耗费的忍耐度
for(int j=;j <= s;++j)//当前杀怪数
if(i >= b[kind])
dp[tot][i][j]=max(dp[tot-][i][j],dp[tot-][i-b[kind]][j-]+a[kind]);
else
dp[tot][i][j]=dp[tot-][i][j];
tot++;
}
}
}
思路完全正解,提交试试,返回的结果竟然是MLE...............
(2):使用滚动数组优化
既然MLE,那我用滚动数组优化一下总行了吧
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=+; int n,m,k,s;
int a[maxn],b[maxn];
int dp[maxn][maxn]; int Solve()
{
mem(dp,);
bool index=;
for(int kind=;kind <= k;++kind)
{
int x=min(m/b[kind],s);
while(x--)//x 个 ki 怪物
{
for(int i=m;i >= b[kind];--i)
for(int j=;j <= s;++j)
dp[i][j]=max(dp[i][j],dp[i-b[kind]][j-]+a[kind]);
}
}
int res=m+;
for(int i=;i <= m;++i)
for(int j=;j <= s;++j)
if(dp[i][j] >= n)
res=(res > i ? i:res);//找到经验值达到n以上的最小的忍耐度
return m-res;
} int main()
{
while(~scanf("%d%d%d%d",&n,&m,&k,&s))
{
for(int i=;i <= k;++i)
scanf("%d%d",a+i,b+i);
printf("%d\n",Solve());
}
}
bingo,正解,不过,来分析一下此种做法的时间复杂度。
对于最坏的情况,m=100,k=100,s=100,且对于所有的 i 有 a[i] = b[i] =1,其时间复杂度高达O(n^4),要不是此题范围小,指定超时。
那么,还有比这更有的算法吗?
有个稍加优化的方法,可以将最坏的时间复杂度变为O(n^3log(n))。
把第ki个怪拆成忍耐度为b[ki]*(2^x)、经验值为a[ki]*(2^x)的若干个怪,其中 x 满足 b[ki]*(2^x) < m && (2^x) < s 。
这是二进制的思想,因为不管最优策略杀几头第 ki 个物品,总可以表示成若干个 2^x 个怪物的和。
这样把每头怪拆成O( log(min(m/b[kind],s)) )头怪,是一个很大的改进。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=+; int n,m,k,s;
int a[maxn],b[maxn];
int dp[maxn][maxn]; int Solve()
{
mem(dp,);
bool index=;
for(int kind=;kind <= k;++kind)
{
int x=log(min(m/b[kind],s))/log();
for(int tot=;tot <= x;++tot)
{
for(int i=m;i >= (<<tot)*b[kind];--i)
for(int j=(<<tot);j <= s;++j)
dp[i][j]=max(dp[i][j],dp[i-(<<tot)*b[kind]][j-(<<tot)]+(<<tot)*a[kind]);
}
}
int res=m+;
for(int i=;i <= m;++i)
for(int j=;j <= s;++j)
if(dp[i][j] >= n)
res=(res > i ? i:res);//找到经验值达到n以上的最小的忍耐度
return m-res;
} int main()
{
while(~scanf("%d%d%d%d",&n,&m,&k,&s))
{
for(int i=;i <= k;++i)
scanf("%d%d",a+i,b+i);
printf("%d\n",Solve());
}
}
思路二:完全背包+滚动数组优化空间
设dp[i][j]表示消耗 i 个忍耐度,杀 j 头怪所获得的最大经验值。
状态转移方程:
dp[i][j]=max(dp[i][j],dp[i-b[k1]][j-1]+a[k1])
dp[i-b[k1]][j-1]+a[k1] : 杀k1怪所获得最大经验值
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=+; int n,m,k,s;
int a[maxn],b[maxn];
int dp[maxn][maxn];//dp[i][j] : 所需耐力值为i杀怪数为j时所获得的最大经验值 int Solve()
{
mem(dp,);
for(int k1=;k1 <= k;++k1)
for(int i=b[k1];i <= m;++i)
for(int j=;j <= s;++j)
dp[i][j]=max(dp[i][j],dp[i-b[k1]][j-]+a[k1]);
for(int i=;i <= m;++i)
for(int j=;j <= s;++j)
if(dp[i][j] >= n)
return m-i;
return -;
}
int main()
{
while(scanf("%d%d%d%d",&n,&m,&k,&s) != EOF)
{
for(int i=;i <= k;++i)
scanf("%d%d",a+i,b+i);
printf("%d\n",Solve());
}
return ;
}
总结:
这种题设dp变量很重要,要设成几维的以及含义。
设成几维的?
有多少个限制条件,就设置成几维的,例如此题有三个限制条件①怪物种类②忍耐度③杀怪数
如果不适用滚动数组,则需要设置成三维数组。
如果使用滚动数组优化空间,则把第一个限制条件开辟的空间省去了,但第一个限制条件要在最外层循环处
hdu 2159FATE(完全背包)的更多相关文章
- HDU 1011 树形背包(DP) Starship Troopers
题目链接: HDU 1011 树形背包(DP) Starship Troopers 题意: 地图中有一些房间, 每个房间有一定的bugs和得到brains的可能性值, 一个人带领m支军队从入口(房 ...
- hdu 5445 多重背包
Food Problem Time Limit: 3000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...
- hdu 1203 01背包 I need a offer
hdu 1203 01背包 I need a offer 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1203 题目大意:给你每个学校得到offe ...
- HDU 1712 分组背包
ACboy needs your help Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Ot ...
- hdu 2191 多重背包 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
http://acm.hdu.edu.cn/showproblem.php?pid=2191 New~ 欢迎“热爱编程”的高考少年——报考杭州电子科技大学计算机学院关于2015年杭电ACM暑期集训队的 ...
- hdu 2844 多重背包coins
http://acm.hdu.edu.cn/showproblem.php?pid=2844 题意: 有n个硬币,知道其价值A1.....An.数量C1...Cn.问在1到m价值之间,最多能组成多少种 ...
- hdu 1864 01背包 最大报销额
http://acm.hdu.edu.cn/showproblem.php?pid=1864 New~ 欢迎“热爱编程”的高考少年——报考杭州电子科技大学计算机学院关于2015年杭电ACM暑期集训队的 ...
- hdu 2955 01背包
http://acm.hdu.edu.cn/showproblem.php?pid=2955 如果认为:1-P是背包的容量,n是物品的个数,sum是所有物品的总价值,条件就是装入背包的物品的体积和不能 ...
- HDU 2955 Robberies 背包概率DP
A - Robberies Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submi ...
随机推荐
- 洛谷 P1441 砝码称重
题目描述 现有n个砝码,重量分别为a1,a2,a3,……,an,在去掉m个砝码后,问最多能称量出多少不同的重量(不包括0). 输入输出格式 输入格式: 输入文件weight.in的第1行为有两个整数n ...
- node.js 运行机制与简单使用
一.hello world 1.引入 required 模块 2.创建服务器 3.接收请求与响应请求 var http = require('http'); // 载入http模块 http.crea ...
- 傻瓜式搭建私人网络硬盘——owncloud安装指南
百度云这个贱货天天删我资源,我已经忍无可忍了,于是想搭建一个owncloud来放我的里番.使用owncloud不仅安全,而且还可以在线播放,离线下载,功能相当强大. 然而·····网上查了一下,竟然无 ...
- 4、new一个对象的时候,初始化顺序:
父类静态块>子类静态块> 父类属性(先系统默认值,后直接你赋予的值) >父类构造器>子类属性>子类构造器
- kubernetes 一个服务的基本组成
1. service Service是kubernetes最核心的概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求进行负载分发到后端的各个容器应用上 k ...
- bzoj 1086: [SCOI2005]王室联邦 (分块+dfs)
Description “余”人国的国王想重新编制他的国家.他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成员来管理.他的国家有n个城市,编号为1..n.一些城市之间有道路相连,任意两个不 ...
- MT【283】图像有唯一公共点.
函数$f(x)=\sqrt[n]x(n-\ln x),$其中$n\in N^*,x\in(0,+\infty)$.(1)若$n$为定值,求$f(x)$的最大值.(2)求证:对任意$m\in N^+$, ...
- 【Gym - 100947G】Square Spiral Search
BUPT 2017 summer training (for 16) #1C 题意 A new computer scientist is trying to develop a new memory ...
- 三天STL与pbds(平板电视)
19.02.11 ~ 19.02.13 hjmmm要专攻STL辣 先列一下大纲? 第一天:各种基础容器 第二天:实现平衡树和平板电视pbds 第三天:非变异算法和变异算法 那么我们就开始吧! Day1 ...
- Shell 字符截取命令 Cut
1.Cut 命令 # cut [选项] 文件名 选项 : -f 列号 提取第几列 -d 分隔符,按照指定分隔符分割列 2.例子 cut -f 1,4 -d ":" /etc/p ...