比赛题目训练系列14 (2020-2021 ICPC, NERC, Northern Eurasia Onsite) D题

训练系列

D. Digits

  • 题意,给 n n n 个数字 ( n ≤ 1 0 5 n \le 10^5 n105),每个数字 a i ≤ 1 0 3 a_i \le 10^3 ai103,选出一些数字,使他们的乘积最大,并且满足乘积的末尾数字为 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=A1,由于浮点数是科学计数法,所以他们的绝对误差就是相对误差
    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}}. lnAlnBlnA=lnAlnAA1=lnAln(1A1)lnAA1A1=103e51.
  • 但是实际上,这种极端情况很难发生,或者说不可能出现.
#include
#include
#include
#include
#include
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;
}

你可能感兴趣的:(ACM题目整理,算法,机器学习,动态规划)