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 ...
随机推荐
- bootstrap簡介
bootstarp是最受歡迎的前端開發框架,可以開發數適用pc.平板電腦和手機的web應用,是基於html.css和javascript.只要學會bootstarp,就代表具有web的開發的中級水準.
- 洛谷 p1219 八皇后
刚参加完蓝桥杯 弱鸡错了好几道..回头一看确实不难 写起来还是挺慢的 于是开始了刷题的道路 蓝桥杯又名搜索杯 暴力杯...于是先从dfs刷起 八皇后是很经典的dfs问题 洛谷的这道题是这样的 上面的布 ...
- react事件绑定,事件传参,input单向数据绑定
import React, { Component } from 'react'; class New extends Component { constructor(props){ super(pr ...
- Modbus CRC 16 (C#)
算法 1.预置一个值为 0xFFFF 的 16 位寄存器,此寄存器为 CRC 寄存器. 2.把第 1 个 8 位二进制数据(即通信消息帧的第 1 个字节)与 16 位的 CRC 寄存器相异或,异或的结 ...
- ElasticSearch查询 第四篇:匹配查询(Match)
<ElasticSearch查询>目录导航: ElasticSearch查询 第一篇:搜索API ElasticSearch查询 第二篇:文档更新 ElasticSearch查询 第三篇: ...
- Spring 使用介绍(十三)—— Bean的生命周期
一.概述 Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,生命周期时序图如下: 二.生命周期接口分类 Bean的生命周期经历了多个接口方法的调用, ...
- Google浏览器——AxureRP_for_chorme_0_6_2添加
准备 链接:https://share.weiyun.com/5PVwSMA Google浏览器版本 步骤 压缩解压 首先把需要安装的第三方插件,后缀.crx 改成 .rar,然后解压,得到一个文件夹 ...
- Navicat MySQL 自动备份
1 新建批处理作业 2 设定(多个库,就轮流选择不同库,然后步骤2) 最后记得保存 3 计划 4 设计批处理作业->设置计划任务 5 输入账号和密码 服务器登录名 服务器密码 6 你可以修改备份 ...
- LOJ2721 [NOI2018] 屠龙勇士 【扩展中国剩余定理】
好久没写了,写一篇凑个数. 题目分析: 这题不难想,讲一下中国剩余定理怎么扩展. 考虑$$\left\{\begin{matrix}x \equiv a\pmod{b}\\ x \equiv c\pm ...
- Python可迭代对象中的添加和删除(add,append,pop,remove,insert)
list: classmates = ['Michael', 'Bob', 'Tracy'] classmates.append('Adam') //添加在末尾,没有add()方法 classmate ...