Codeforces1461F.Mathematical Expression dp,贪心
Codeforces1461F.Mathematical Expression
题意
给定一个长度为\(n\)的整数数组\(a\)和一个长度不大于\(3\)的由运算符组成的字符串\(s\),让你在\(a\)中所有相邻的数字之间添加一个\(s\)中的运算符,使这个算术表达式的值最大。
\(n\le 10^5,0\le a_i \le 9\),\(s\)由"-","+","*"组成。
分析
当\(s\)中只包括一种运算符时,只能填充这个运算符,"+-*"可以等价于"+*",“+-”等价于"+",那么就只剩下两种情况。
- “-*",两个大于\(0\)的数之间一定是填充"*",设\(x>0\),\(0x\)之间若填充”-“会出现负数,所以要用"*",\(x0\)之间用"-"。
- "+*",对于这种情况,我们以\(0\)作为分割点,\(0\)的两边一定是"+",对于分割出的每一段分别考虑,假设某一段在原数组中为区间\([l,r]\),对于这个区间开头的一段\(1\)和结尾的一段\(1\)每个数字之间都填充"+",对于中间的一段我们自然可以想到若它们的乘积非常大(大于\(10^{16}\)),一定是全部乘起来,否则对于\(2,3,1,1,\cdots,1,1,2,3\)这种中间有很长的一段\(1\)的数据,\(2\times 3+1+1+\cdots+1+1+2\times 3\)这样会更大,考虑动态规划,设\(dp[i]\)表示前\(i\)个数后已经添加运算符的最大值,从\(i\)转移到\(j(j>i)\),有两种情况,\([i+1,j-1]\)这一段每个数字后面都填充加号或者都填充乘号,这一段后添加加号,这样是\(n^2\)的,因为连续的一段\(1\)一定是全部加起来或者和两边乘起来,设\(nex[i]\)表示\(i\)所在连续\(1\)的段的段尾,\(j\)就可以不断的跳\(max(j+1,nex[i])\)来加速转移,因为这个区间所有数字乘起来都不大于\(10^{16}\),所以\(j\)最多跳\(log(10^{16})\)次,记录转移的前驱,回溯更新答案即可。
Code
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=1e5+10;
const ll inf=1e16;
int n,a[N];
vector<int>w;
string s;
ll pre[N],sum[N],dp[N];
int f[N],nex[N];
char c[N];
char ans[N];
void gao(int l,int r){
int dl=l,dr=r;
while(a[dl]==1&&dl<=r){
ans[dl]='+';
++dl;
}
while(a[dr]==1&&l<=dr){
ans[dr]='+';
--dr;
}
l=dl,r=dr;
if(l>r) return;
pre[l-1]=1;
sum[l-1]=0;
int flag=0;
rep(i,l,r){
pre[i]=pre[i-1]*a[i];
sum[i]=sum[i-1]+a[i];
if(pre[i]>inf){
flag=1;
break;
}
}
if(flag){
rep(i,l,r-1) ans[i]='*';
ans[r]='+';
return;
}
int now=l;
rep(i,l,r){
now=max(now,i);
while(a[now]==1&&now<=n) ++now;
if(now==i) nex[i]=now;
else nex[i]=now-1;
}
rep(i,l-1,r) dp[i]=0;
rep(i,l-1,r-1){
for(int j=i+1;j<=r;j=max(j+1,nex[j])){
if(pre[j]/pre[i]+dp[i]>dp[j]){
dp[j]=pre[j]/pre[i]+dp[i];
f[j]=i;
c[j]='*';
}
if(sum[j]-sum[i]+dp[i]>dp[j]){
dp[j]=sum[j]-sum[i]+dp[i];
f[j]=i;
c[j]='+';
}
}
}
now=r;
while(now!=l-1){
int x=f[now];
rep(i,x+1,now-1) ans[i]=c[now];
ans[now]='+';
now=x;
}
}
int main(){
//ios::sync_with_stdio(false);
//freopen("in","r",stdin);
cin>>n;
rep(i,1,n) cin>>a[i];
cin>>s;
if(sz(s)==1){
rep(i,1,n-1){
cout<<a[i]<<s[0];
}
cout<<a[n]<<endl;
}else if(s=="+-"||s=="-+"){
rep(i,1,n-1){
cout<<a[i]<<"+";
}
cout<<a[n]<<endl;
}else if(s=="+*"||s=="*+"||sz(s)==3){
int l=1;
for(int i=1;i<=n;i++){
if(a[i]==0){
if(l<=i-1) gao(l,i-1);
ans[i]='+';
l=i+1;
}
}
if(l<=n) gao(l,n);
rep(i,1,n-1) cout<<a[i]<<ans[i];
cout<<a[n]<<endl;
}else if(s=="-*"||s=="*-"){
cout<<a[1];
rep(i,2,n){
if(a[i-1]>0&&a[i]==0) cout<<'-';
else cout<<"*";
cout<<a[i];
}
cout<<endl;
}
return 0;
}