题解 CF1510D Digits

\(n\) 个数中选出若干个,使乘积个位是 \(d\),如有多种,输出积最大的那种。

开始想的是取个模,由于 \(0 \to 9\) 的幂次在模 \(10\) 下的循环都很少,可以预处理出所有的可能数,但是这样没有办法找出乘积最大的。

这最大的问题还是得靠 dp,\(f_{i, j}\) 表示前 \(i\) 个积个位是 \(j\) 的最大积,很容易转移。但显然爆 long long

tricks

题目中 没有乘积大小的精确要求 时,可以用取对数后的值来代替积。

在这题中我们只要方案不要积,所以在转移判断大小时用对数代替就可以了。

这次长记性了,用记录状态来构造方案。但还是调了很久,主要是没有考虑清楚初始情况,诸老的 dp 三步还是不能丢,必须搞清楚了这些才能开始写代码。

另外在写的过程中对背包问题写成“我为人人”形式的转移也有一些不清楚的地方,仍在用人人为我的思路去写我为人人,这是不对的。谁的决策就要以谁为准。

#include <iostream>
#include <cmath>
#include <vector>
#include <iomanip>
std::vector<int> ans;
const int N = 100005;
int n, d, a[N], pf[N][10], pj[N][10];
double f[N][10];
void trans(double &x, double y, int ni, int nj, int fl, int j) {
    if (y < x) return;
    x = y;
    pf[ni][nj] = fl, pj[ni][nj] = j;
}
int main() {
    std::cin >> n >> d;
    for (int i = 1; i <= n; i++) std::cin >> a[i];
    for (int i = 0; i <= n; i++)
        for (int j = 0; j < 10; j++) f[i][j] = -1e18;
    for (int i = 0; i <= n; i++) f[i][1] = 0;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < 10; j++)
            trans(f[i+1][j*a[i+1]%10], f[i][j] + log10(a[i+1]), i+1, j*a[i+1]%10, 1, j),
            trans(f[i+1][j], f[i][j], i+1, j, 0, j);
    if (f[n][d] < 1e-8) return std::cout << -1, 0; 
    for (int i = n, j = d; i >= 1; i--) {
        if (pf[i][j]) ans.push_back(a[i]);
        j = pj[i][j];
    }
    std::cout << ans.size() << '\n';
    for (int i = 0; i < (int)ans.size(); i++) 
        std::cout << ans[i] << ' ';
}
posted @ 2021-08-11 11:18  Acfboy  阅读(57)  评论(0编辑  收藏  举报