题解 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] << ' ';
}