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;
}
posted @ 2020-12-18 13:54  xyq0220  阅读(128)  评论(0编辑  收藏  举报