训练系列
D. Digits
- 题意,给
n
n
n 个数字 (
n
≤
1
0
5
n \le 10^5
n≤105),每个数字
a
i
≤
1
0
3
a_i \le 10^3
ai≤103,选出一些数字,使他们的乘积最大,并且满足乘积的末尾数字为 K (K 是输入数据)。输出选择哪些数字。
- 这里讲解一个可以AC但是精度有可能会有问题(出题人没有卡掉)的解法。乘积最大等价于取对数求和后最大(精度就是这样出的问题,下面会解释原因)。就是类比背包,但是这个是从第 i 层去推导第 i + 1 层的状态,而不是第 i 层的状态从 i - 1 层转移过来。因为这样子更好写一些。原理很简单,看代码就行。但是有一个地方,就是记录路径需要思考一下。我在输出路径的时候除了问题,原因是路径需要两个参数
i
d
id
id 和
k
k
k 确定:
id = f[id][k].id;
k = f[id][k].k;
- 是不是显然不对。因为id已经修改了,怎么可以继续
k
=
f
[
i
d
]
[
k
]
.
k
k = f[id][k].k
k=f[id][k].k 呢?
- 另外,精度是这样分析的。答案最大应该是
100
0
100000
=
1
0
3
e
5
1000^{100000} = 10^{3e5}
1000100000=103e5,我们记为
A
A
A,令
B
=
A
−
1
B = A - 1
B=A−1,由于浮点数是科学计数法,所以他们的绝对误差就是相对误差
ln
B
−
ln
A
ln
A
=
ln
A
−
1
A
ln
A
=
ln
(
1
−
1
A
)
ln
A
≈
−
1
A
ln
A
≤
−
1
A
=
−
1
1
0
3
e
5
.
\frac{\ln B - \ln A}{\ln A} = \frac{\ln \frac{A - 1}{A}}{\ln A} = \frac{\ln(1 - \frac{1}{A})}{\ln A} \approx \frac{-\frac{1}{A}}{\ln A} \le -\frac{1}{A} = -\frac{1}{10^{3e5}}.
lnAlnB−lnA=lnAlnAA−1=lnAln(1−A1)≈lnA−A1≤−A1=−103e51. - 但是实际上,这种极端情况很难发生,或者说不可能出现.
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 100010;
struct P
{
int id;
double val;
int k;
};
P f[maxn][15];
int N, K;
int w[maxn];
double logw[maxn];
int sign(double x)
{
if(fabs(x) < 1e-8) return 0;
if(x < 0) return -1;
else return 1;
}
int main()
{
scanf("%d%d", &N, &K);
for(int i = 1; i <= N; i++){
scanf("%d", &w[i]);
logw[i] = log(w[i]);
}
for(int i = 1; i <= N; i++){
int x = w[i] % 10;
for(int j = 0; j < 10; j++) f[i + 1][j] = f[i][j];
for(int j = 0; j < 10; j++){
int tmp;
if(!sign(f[i][j].val))
{
tmp = x;
}
else tmp = j * x % 10;
if(f[i + 1][tmp].val < f[i][j].val + logw[i]){
if(sign(f[i][j].val)) f[i + 1][tmp] = {i, f[i][j].val + logw[i], j};
else f[i + 1][tmp] = {i, logw[i], 1};
}
}
}
int id = f[N + 1][K].id, k = f[N + 1][K].k;
vector<int> ans;
if(id == 0){
if(K == 1){
for(int i = 1; i <= N; i++){
if(w[i] == 1) ans.push_back(w[i]);
}
}
}
else while(id != 0){
ans.push_back(w[id]);
int old_id = id, old_k = k;
id = f[old_id][old_k].id;
k = f[old_id][old_k].k;
}
int sz = ans.size();
if(sz == 0){
printf("-1\n");
}
else{
printf("%d\n", sz);
for(int i = 0; i < sz; i++){
printf("%d%c", ans[i], i + 1 == sz ? '\n' : ' ');
}
}
return 0;
}