P5365 [SNOI2017] 英雄联盟
P5365 [SNOI2017] 英雄联盟
题目描述
正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。
现在,小皮球终于受不了网友们的嘲讽,决定变强了,他变强的方法就是:买皮肤!
小皮球只会玩
这
为了让自己看起来高大上一些,小皮球决定给同学们展示一下自己的皮肤,展示的思路是这样的:对于有皮肤的每一个英雄,随便选一个皮肤给同学看。
比如,小皮球共有 5 个英雄,这 5 个英雄分别有
现在,小皮球希望自己的展示策略能够至少达到
输入格式
第一行,两个整数
第二行,
第三行,
输出格式
一个整数,表示小皮球达到目标最少的花费。
说明/提示
样例解释
每一个英雄都只有4款皮肤,每款皮肤2 Q币,那么每个英雄买3款皮肤,
数据范围
共 10 组数据,第
思路
多重背包,只是价值的计算是乘法
这里要求找到满足条件的最小花费,有点类似完全背包的P2918
P2918 [USACO08NOV]Buying Hay S
但是如果仿照2918的写法,这里的K最大能到10^17,时间空间复杂度都会过高
考虑把方案数量作为价值,花费作为成本,但是这样不方便找最大花费的条件,先算一下最大的可能:
>>> import math
>>> math.log(10,2)**4
121.77577736852207
>>> 121 * 199 * 10 # 最多10个皮肤,每个最贵199
240790
>>> (math.log(10,2)**4) * 199 * 10
242333.7969633589可以接受的程度,最后再遍历一遍数组,找到最小的满足条件的即可。
另一个难思考的地方是状态转移方程式
在加法的多重背包中,显然;
j = a * C + b;
我们要找:
max(dp[j - k * C] + k * V)
带入
dp[j - k * C] + k * V
= dp[a * C + b - k * C] + k * V
= dp[a * C + b - k * C] + k * V
= dp[(a - k) * C + b] + k * V
令r = a - k
原式
= dp[r * C + b] + (a - r) * V
= dp[r * C + b] - r * V + a * V
a和V是无关量,所以在单调队列找最大的dp[r * C + b] - r * V就可以了但是本题的多重背包是这样:
max(dp[j - k * C] * k)
= dp[r * C + b] * (a - r)这里没法拆开a, 所以不方便使用单调背包优化
而二进制拆分法的话会拆成多个物品,不方便相乘
最后尝试直接暴力拆分,居然过了。。。
题解
#include <iostream>
#include <cstring>
#include <deque>
#define debug(x) #x << ":" << x << " "
#define ll long long
using namespace std;
struct
{
ll K; // 数量
ll C; // 价格
} heroes[256];
ll dp[2][300000];
int now = 0;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll N, M;
cin >> N >> M;
for (int i = 1; i <= N; i++)
{
cin >> heroes[i].K;
}
for (int i = 1; i <= N; i++)
{
cin >> heroes[i].C;
}
int maxcost = 0; // 计算一下最大的花费
for (int i = 1; i <= N; i++)
{
maxcost += heroes[i].C * heroes[i].K;
}
for (int j = 0; j <= maxcost; j++)
{
// if (dp[now][j] >= M)
// {
dp[now][j] = 1;
// break;
// }
}
for (int i = 1; i <= N; i++)
{
int pre = now;
now = 1 - now;
for (int j = 0; j <= maxcost; j++)
{
dp[now][j] = dp[pre][j];
for (int c = 1; c <= heroes[i].K && c <= j / heroes[i].C; c++)
{
dp[now][j] = max(dp[now][j], dp[pre][j - c * heroes[i].C] * c);
}
}
}
for (int j = 0; j <= maxcost; j++)
{
if (dp[now][j] >= M)
{
cout << j;
break;
}
}
return 0;
}