1. 程式人生 > 實用技巧 >#dp#NOIP2020.9.26模擬jerry

#dp#NOIP2020.9.26模擬jerry

題目

Jerry 寫下了一個只由非負整數和加減號組成的算式。
它想給這個算式新增合法的括號,使得算式的結果最大。


分析

考場\(O(n^3)\)偽部分分成功爆零,
\(dp[i][j]\)表示前\(i\)個數中缺了\(j\)個右括號需要配對的最大結果,
那就考慮是加一個括號(第\(i\)個數前是負號)或不變或配對一個括號,
但是感性理解一下缺了三個或以上的右括號實際上可以被兩個以內所代替
(三重否定為否定)。由於正數是可以合併的,所以括號是可以被拆開成兩部分的
那麼這道題就可以\(O(n)\)解決了


程式碼

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011; typedef long long lll;
int n,a[N],b[N]; lll dp[N][3];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline lll max(lll a,lll b){return a>b?a:b;}
signed main(){
	freopen("jerry.in","r",stdin);
	freopen("jerry.out","w",stdout); 
	for (rr int T=iut();T;--T){
		n=iut(),b[1]=0;
		for (rr int i=1;i<=n;++i){
			a[i]=iut();
			if (i==n) continue;
			rr char c=getchar();
			while (c!='+'&&c!='-') c=getchar();
			b[i+1]=(c=='-');
		}
		dp[0][0]=0,dp[0][1]=dp[0][2]=-1e18;
		for (rr int i=1;i<=n;++i)
		if (b[i]){
			dp[i][0]=-1e18;//前面是負號強行加括號,所以不可能
			dp[i][1]=max(max(dp[i-1][0],dp[i-1][1]),dp[i-1][2])-a[i];
			dp[i][2]=max(dp[i-1][1],dp[i-1][2])+a[i];//變號
		}else{
			dp[i][0]=max(max(dp[i-1][0],dp[i-1][1]),dp[i-1][2])+a[i];
			dp[i][1]=max(dp[i-1][1],dp[i-1][2])-a[i];//變號
			dp[i][2]=dp[i-1][2]+a[i];//兩次變號符號不變
		}
		printf("%lld\n",max(max(dp[n][0],dp[n][1]),dp[n][2]));
	}
	return 0;
}