传送门

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(完全背包)的更多相关文章

  1. HDU 1011 树形背包(DP) Starship Troopers

    题目链接:  HDU 1011 树形背包(DP) Starship Troopers 题意:  地图中有一些房间, 每个房间有一定的bugs和得到brains的可能性值, 一个人带领m支军队从入口(房 ...

  2. hdu 5445 多重背包

    Food Problem Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)To ...

  3. hdu 1203 01背包 I need a offer

    hdu 1203  01背包  I need a offer 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1203 题目大意:给你每个学校得到offe ...

  4. HDU 1712 分组背包

    ACboy needs your help Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Ot ...

  5. hdu 2191 多重背包 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

    http://acm.hdu.edu.cn/showproblem.php?pid=2191 New~ 欢迎“热爱编程”的高考少年——报考杭州电子科技大学计算机学院关于2015年杭电ACM暑期集训队的 ...

  6. hdu 2844 多重背包coins

    http://acm.hdu.edu.cn/showproblem.php?pid=2844 题意: 有n个硬币,知道其价值A1.....An.数量C1...Cn.问在1到m价值之间,最多能组成多少种 ...

  7. hdu 1864 01背包 最大报销额

    http://acm.hdu.edu.cn/showproblem.php?pid=1864 New~ 欢迎“热爱编程”的高考少年——报考杭州电子科技大学计算机学院关于2015年杭电ACM暑期集训队的 ...

  8. hdu 2955 01背包

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

  9. HDU 2955 Robberies 背包概率DP

    A - Robberies Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submi ...

随机推荐

  1. Algorithm Visualizer

    Algorithm Visualizer https://algorithm-visualizer.org/ https://algorithm-visualizer.org/divide-and-c ...

  2. 销售合同金额数据从Excel导入

    一.业务需求 1.新增了销售合同金额的字段,但是老数据没有这个字段:所以销售合同金额从销售合同附件的各品种金额之和. 2.制作好excel字段模板,将此模板发送给销售业务部门来统计并完成excel表格 ...

  3. SWT 几个sample网站

    https://www.programcreek.com/java-api-examples/org.eclipse.swt.custom.ScrolledComposite https://o7pl ...

  4. MySQL 大数据量分页优化

    假设有一个千万量级的表,取1到10条数据: ,; ,; 这两条语句查询时间应该在毫秒级完成: ,; 你可能没想到,这条语句执行之间在5s左右: 为什么相差这么大? 可能mysql并没有你想的那么智能, ...

  5. 如何下载旧版本的MySQL

    可能存在这样的场景,比如一些老系统需要使用MySQL 5.5版本才能运行,其余的不行. 1.登录下载站点 https://dev.mysql.com/downloads/mysql/ 此时的最新版本为 ...

  6. web scraper——安装【一】

    准备工作 工欲善其事必先利其器,既然是要安装web scraper一些***的工具是必然不可缺少的,如果没有的话,先下载个蓝灯用用吧. 蓝灯最新版下载地址 下载安装完成后双击打开即可,这时候会弹出一个 ...

  7. Mysql 计划任务

    -- 设置 show variables like '%sche%'; ; -- Start存储过程 drop PROCEDURE if exists test; CREATE PROCEDURE t ...

  8. 洛谷P2045 K方格取数(算竞进阶习题)

    费用流 又是一道网络流的模型,对于这种费用与经过次数有关的边,我们经常把边拆成多条,比如这个题,第一次费用是x,第二次是0,我们就可以先把点拆成入点和出点,入点和出点又连两条边,第一条容量为1,费用为 ...

  9. OpenCv的CV2一些函数总结

  10. Python中操作ini配置文件

    这篇博客我主要想总结一下python中的ini文件的使用,最近在写python操作mysql数据库,那么作为测试人员测试的环境包括(测试环境,UAT环境,生产环境)每次需要连接数据库的ip,端口,都会 ...