• 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;
    }
    
  • 相关阅读:
    mycat实例(1)
    Java连接Oracle数据库的示例代码
    文本处理grep命令
    回调函数
    算法基础--快排序,堆排序,归并排序
    c++ 中double与string之间的转换,char *
    c++ 类型转换
    allocator class
    csapp读书笔记-并发编程
    树的遍历-递归方法,非递归方法
  • 原文地址:https://www.cnblogs.com/bifanwen/p/16036679.html
Copyright © 2020-2023  润新知