Codeforces1461F.Mathematical Expression dp,贪心

Codeforces1461F.Mathematical Expression

题意

给定一个长度为(n)的整数数组(a)和一个长度不大于(3)的由运算符组成的字符串(s),让你在(a)中所有相邻的数字之间添加一个(s)中的运算符,使这个算术表达式的值最大。

(nle 10^5,0le 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 imes 3+1+1+cdots+1+1+2 imes 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;
}
原文地址:https://www.cnblogs.com/xyq0220/p/14154551.html