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 ...
随机推荐
- 微服务 Micro services
微服务 (Microservices) 是一种软件架构风格,它是以专注于单一责任与功能的小型功能区块 (Small Building Blocks) 为基础,利用模组化的方式组合出复杂的大型应用程序, ...
- codeforces525B
Pasha and String CodeForces - 525B Pasha got a very beautiful string s for his birthday, the string ...
- sql server2012学习笔记
第1章 SQL安装 1-1 [SQL Server基础]前言 (10:44) 1-2 SQL Server安装 (08:29) 1-3 第一次登陆 SQL Server (02:58) 1-4 [ ...
- 双击jar文件运行程序
Java应用程序jar文件可以由 JVM(Java虚拟机)直接执行,只要操作系统安装了JVM便可以运行作为Java应用程序的jar文件.可是,很多朋友遇到一个难题,那就是下载了jar文件以后在Wind ...
- PHP——实现随机打乱一个二维数组
<?php /* * @Author: wyy * @Date: 2019-01-28 10:26:29 * @Email: 2752154874@qq.com * @Last Modified ...
- Matplotlib学习---用matplotlib画阶梯图(step plot)
这里利用Nathan Yau所著的<鲜活的数据:数据可视化指南>一书中的数据,学习画图. 数据地址:http://datasets.flowingdata.com/us-postage.c ...
- 爬虫_淘宝(selenium)
总体来说代码还不是太完美 实现了js渲染网页的解析的一种思路 主要是这个下拉操作,不能一下拉到底,数据是在中间加载进来的, 具体过程都有写注释 from selenium import webdriv ...
- [NOIp2012] 国王游戏(排序 + 贪心 + 高精度)
题意 给你两个长为 \(n+1\) 的数组 \(a,b\) ,你需要定义一个顺序 \(p\) (\(p_0\) 永远为 \(0\)) 能够最小化 \[ \max_{i=1}^{n} \frac{\pr ...
- 【BZOJ3157/3516】国王奇遇记(数论)
[BZOJ3157/3516]国王奇遇记(数论) 题面 BZOJ3157 BZOJ3516 题解 先考虑怎么做\(m\le 100\)的情况. 令\(f(n,k)=\displaystyle \sum ...
- 【转】使用STM32F4的CCM内存
我们知道STM32F4当中有个CCM内存,如图所示,这个内存是挂在D总线上直接和内核相连,因此除了内核之外谁都不能访问,那么我们怎么将其利用起来呢?网上这个资料还真的很少,今天我就给大家分享一下,献给 ...