前言: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;
}