D.Digits

前言:log防爆orz orz


题目传送门

D.Digits
  题目类型:dp,log防爆。
  解析:对于这种按照某种限制随意取最大值的题,我们考虑dp。以dp[i][j]表示前i个中选一些数,乘积的个位为j的情况下,乘积的最大值。
  现在有一个问题,1e5个数的乘积最大是1000^1e5 = 10^3e5,显然无法存储,由于只有乘法运算,我们可以用log来表示,因为log(a*b) = loga + logb,同时乘积的最大值也变成了log(10^3e5) 约等于 1e6 , 相应的,dp数组要用double类型
  像背包一样,对于每个数,可选可不选
  当不选时,继承上一位,有dp[i][j] = dp[i-1][j]。
  当选定时,先考虑单选i,有dp[i][j] = max(dp[i][j],log(a[i]);
  然后考虑从上一位转移,令nex = j*(a[i]%10)%10。dp[i][nex] = max(dp[i][nex] ,dp[i-1][j] + log(a[i]))。
  再用一个链表结构存储每一个dp的上一个取值状态,用来输出。
  code:

#include <bits\stdc++.h>

using namespace std;

int n,d,cnt,a[101010],ans[101010],nid[101010][15];
double dp[101010][15];
typedef pair<int,int>pii;
pii last[101010][15];

void init()
{
    for(int i = 0 ; i <= n ; ++i)
        for(int j = 0 ; j <= 9 ; ++j)
            dp[i][j] = -1e9;
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> d ;
    for(int i = 1 ; i <= n ; ++i)
        cin >> a[i] ;
    init();
    for(int i = 1 ; i <= n ; ++i){
        double x = log2(a[i]);
        for(int j = 0 ; j <= 9 ; ++j){
            dp[i][j] = dp[i-1][j];
            last[i][j] = last[i-1][j];
            nid[i][j] = nid[i-1][j];
        }
        for(int j = 0 ; j <= 9 ; ++j){
            if(a[i]%10 == j){
                if(dp[i][j] < x){
                    dp[i][j] = x;
                    last[i][j] = {0,0};
                    nid[i][j] = i;
                }
            }
            if(dp[i-1][j] != -1e9){
                int nex = j*(a[i]%10)%10;
                if(dp[i][nex] <= dp[i-1][j] + x){
                    dp[i][nex] = dp[i-1][j] + x;
                    last[i][nex] = {nid[i-1][j] , j};
                    nid[i][nex] = i;
                }
            }
        }
    }
    int i = n , now = d;
    while(nid[i][now]){
        ans[++cnt] = nid[i][now];
        int j = last[i][now].first , k = last[i][now].second;
        i = j , now = k;
    }
    if(!cnt)cout << -1 << endl ;
    else {
        cout << cnt << endl ;
        for(int i = 1 ; i <= cnt ; ++i)
            cout << a[ans[i]] << " " ;
        cout << endl ;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值