题目
长度为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;
}
本文探讨了一个有趣的问题:如何通过在给定数字序列间插入特定运算符来构建一个使得计算结果最大的数学表达式。针对不同数量及类型的运算符,文章提出了有效的解决方案,并详细介绍了算法实现过程中的关键步骤和技术细节。
1463

被折叠的 条评论
为什么被折叠?



