题目
长度为n(n<=1e5)的a[](0<=ai<=9),
你需要在n-1个空位填入n-1个符号,不能加小括号
使原式值最大,输出最终的式子
如果有多种答案,输出其中一种即可
被允许的符号是一个{‘+’,'-','*'}的非空子集,
由一个字符串s(1<=|s|<=3)给出
思路来源
官方题解
题解
这vscode的sb一键缩进,真香
只有一种符号显然,
两种符号的时候,
+-显然只用+,
*-的话,除了0要减掉以外剩下的都用乘
最后剩两种情况,+*和+-*,
可以发现减号多余,就统一成了一种情况
对于这种情况,首先把0单独弄出来用+分隔开,
对于尺取的剩下的非0段,
①如果乘积大于1e16(其实感觉大于1e5*9*1e5*9好像就行?不太懂这个界是咋求的),显然全用乘号是最优的,
但注意到这种情况下,左右两边的连续1应该用加,
这部分对乘没有贡献,对加有额外贡献
②乘积小于1e16,考虑dp,
dp[i]表示当前扫到的前缀最大值,pre[i]是取到这个值的前驱,add[i]表示i后面是否放一个加号
往后续一段乘积的时候,
考虑后继只可能有最多log1e16个,于是就可以更新了
dp[i]还能只续一个,表示续一个值用来加,用于统一加1和一些小的边界情况
比如2+1+2是比2*1*2更优的,在考虑最后一个2的时候
代码通篇充满了小trick,
比如尺取统一0的情况,比如统计字母用放桶的方式,
比如dp[i]和pre[i]表示在i后面放符号,etc...
还是想把那个apt输出函数用于全篇统一,但是不太会啊orz
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const ll inf = 1e16;
int n, m, a[N], pre[N];
ll dp[N];
bool add[N];
int q[N], h, t;
char s[5];
map<char, int> num;
void apt(int l, int r, char x)
{
for (int i = l; i <= r; ++i)
{
printf("%d", a[i]);
if (i != r)
putchar(x);
}
}
bool upd(ll &x, ll y)
{
if (x < y)
{
x = y;
return 1;
}
return 0;
}
void solve(int l, int r)
{
int nl = l, nr = r;
//注意0的存在 使段乘积>=inf 而总乘积=0
bool ok = false;
ll all = 1;
for (int i = l; i <= r; ++i)
{
all *= a[i];
if (all >= inf)
{
ok = true;
break;
}
}
if (ok)
{
//注意两边1的存在 不能出现在连积的case里 所以事先特判
while (l <= r && a[l] == 1)
{
add[l++] = 1;
}
while (l <= r && a[r] == 1)
{
add[--r] = 1;
}
}
else
{
h = 1;
t = 0;
for (int i = l; i <= r; ++i)
if (a[i] >= 2)
q[++t] = i;
dp[l - 1] = 0;
for (int i = l; i <= r; ++i)
{
if (upd(dp[i], dp[i - 1] + a[i]))
{
pre[i] = i - 1;
}
while (h <= t && q[h] < i)
{
h++;
}
ll now = 1;
//a[i-1] + a[i]*...*a[p] +
for (int j = h; j <= t; ++j)
{
int p = q[j];
now *= a[p];
if (upd(dp[p], dp[i - 1] + now))
{
pre[p] = i - 1;
}
}
}
for (int i = r; i != l - 1; i = pre[i])
{
add[i] = 1;
}
}
for (int i = nl; i <= nr; ++i)
{
printf("%d", a[i]);
if (i != nr)
{
putchar(add[i] ? '+' : '*');
}
}
}
int main()
{
memset(dp, -1, sizeof dp);
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
}
scanf("%s", s);
m = strlen(s);
for (int i = 0; i < m; ++i)
{
num[s[i]]++;
}
//+ - *
if (m == 1)
{
apt(1, n, s[0]);
return 0;
}
//+-
if (m == 2 && !num['*'])
{
apt(1, n, '+');
return 0;
}
//*-
if (m == 2 && !num['+'])
{
for (int i = 1; i <= n; ++i)
{
printf("%d", a[i]);
if (i != n)
{
putchar(a[i + 1] == 0 ? '-' : '*');
}
}
return 0;
}
//+* +*-
for (int i = 1, j; i <= n; i = j + 1)
{
j = i;
if (a[i])
{
while (j + 1 <= n && a[j + 1])
j++;
}
solve(i, j);
if (j != n)
putchar('+');
}
return 0;
}