P5365 [SNOI2017]英雄联盟 题解

原题链接

简要题意:

长度为 \(n\) 的序列 \(a\),初始均为 \(0\). 可以花 \(C_i\) 的代价使 \(a_i \gets a_i + 1\). 求最少花多少代价能使 \(\prod a_i \geq m\)(其中不计 \(a_i = 0\) 的积)。

\(n \leq 200, C_i \leq 125, K_i \leq 10, m \leq 10^{17}\).

个人觉得其实可以先算一下代价的最大值。

很显然代价最大值为 \(\sum C_i \cdot K_i\),也就是 \(2.5 \times 10^5\) 左右。

于是背包 \(\text{dp}\) 应运而生。

很显然,令 \(f_{i,j}\) 表示前 \(i\) 个数,换 \(j\) 的代价所能达到的最大乘积(除 \(0\)). 则很容易得到

\[f_{i,j} = \max(f_{i-1,j}, \max_{k=1}^{K_i} f_{i-1,j-k \cdot c_i} \cdot k) \]

时间复杂度:\(\mathcal{O}(n \sum C_i \cdot K_i)\),非常稳。

空间可能会爆,那就滚动数组,把 \(i\) 这一维省掉。但要注意倒序枚举。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 3e5 + 1;
const int M = 2e2 + 1;

int n; ll m;
int K[M], c[M];
ll f[N];

int main() {
	scanf("%d %lld",&n,&m);
	for(int i = 1; i <= n; i++) scanf("%d", K + i);
	for(int i = 1; i <= n; i++) scanf("%d", c + i);
	f[0] = 1;
	for(int i = 1; i <= n; i++)
	for(int j = N - 1; j > 0; j--)
	for(int k = 1; k <= K[i] && k * c[i] <= j; k++)
		f[j] = max(f[j], 1ll * f[j - k * c[i]] * k);

	for(int i = 1; i < N; i++)	
		if(f[i] >= m) return printf("%d\n",i), 0;
}
posted @ 2022-03-21 21:25  bifanwen  阅读(133)  评论(0编辑  收藏  举报