传送门

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. 利用Python制作简单的小程序:IP查看器

    前言 说实话,查看电脑的IP,也挺无聊的,但是够简单,所以就从这里开始吧.IP地址在操作系统里就可以直接查看.但是除了IP地址,我们也想通过IP获取地理地址和网络运营商情况.IP地址和地理地址并没有固 ...

  2. qtp 自动化测试桌面程序-点滴1(录制设置、共用文件)

    1 automation-record and run settings--设置录制程序 2 将function/repository 放于单独于test的文件夹中-方便多个test使用同一个仓库.函 ...

  3. JMeter 连接 sql server

    1.安装驱动 http://www.microsoft.com/zh-CN/download/details.aspx?id=11774 下载后解压后复制sqljdbc.jar到 “jmeter的安装 ...

  4. for 循环增强

    package cn.zhou.com; /* * 增强for循环 * * for(int i:arr) * { * System.out.print(i+1+" "); * } ...

  5. centos7优化启动项,关闭一些不必要开启的服务

    CentOS7已不再使用chkconfig 管理启动项 使用 systemctl list-unit-files 可以查看启动项 systemctl list-unit-files | grep en ...

  6. eclipse导入项目时,仅项目名出现红叉

    今天导入项目,项目名是红叉,百度了解决办法: 1.导入项目之前,请确认工作空间编码已设置为utf-8:window->Preferences->General->Wrokspace- ...

  7. luogu1503

    P1503 鬼子进村 题目背景 小卡正在新家的客厅中看电视.电视里正在播放放了千八百次依旧重播的<亮剑>,剧中李云龙带领的独立团在一个县城遇到了一个鬼子小队,于是独立团与鬼子展开游击战. ...

  8. hdu-6165(tarjan+topusort)

    题意:一个有向图,无自环,无重边,让你判断这个图内的任意两点是否有路: 解题思路:首先,判断两个点是否可达一般用出入度来判断,如果在拓扑排序中同时有两个及以上入度同时为零的点,那么,这些入度的为零的点 ...

  9. web scraper——简单的爬取数据【二】

    web scraper——安装[一] 在上文中我们已经安装好了web scraper现在我们来进行简单的爬取,就来爬取百度的实时热点吧. http://top.baidu.com/buzz?b=1&a ...

  10. 欧拉筛法模板&&P3383 【模板】线性筛素数

    我们先来看欧拉筛法 •为什么叫欧拉筛呢?这可能是跟欧拉有关 •但是为什么叫线性筛呢?因为它的复杂度是线性的,也就是O(n),我们直接来看代码   #include<cstdio> #inc ...