jerry[unLOCK]

Online Judge:20191014 长沙市一中 CSP模拟赛#day2

Label:思维题,贪心,区间Dp

题目描述

众所周知,Jerry 鼠是一只非常聪明的老鼠。
Jerry 聪明到它可以计算64 位有符号整形数字的加减法。
现在,Jerry 写下了一个只由非负整数和加减号组成的算式。它想给这个算式添加合法的括号,使得算式的结果最大。这里加减法的运算优先级相同,和我们在日常生活中接触到的一样,当没有括号时,先算左边的,再算右边的。
比如,算式((1+2)+3-(4-5))+6 是合法的,但是)1+2((-)1+2 以及-(1)+2 都是不合法的。

输入

输入文件为jerry.in。
第一行一个整数T,代表数据组数。
接下来,共有T 组数据,每组的格式如下:
第一行一个整数n,代表数字的个数。
接下来一行共\(2n-1\) 个符号或非负整数,组成一个由空格隔开的算式。

输出

输出文件为jerry.out。
一行一个整数,代表添加括号后,算式最大的可能结果

样例

Input#1

1 
3
5 - 1 - 3

Output#1

7

Input#2

2 
4
1 - 1 - 1 - 3
4
1 - 1 - 1 + 3

Output#2

4
4

对于第一个样例:5-(1-3)=7

第二个样例:1-(1-1-3)=41-(1-(1+3))=4

Hint

对于50%数据,\(n<=100,T<=100\)

对于70%数据,\(n<=1000,T<=100\)

对于100%数据,\(∑n<=2*10^5\)

对于全部数据,\(2<=n<=10^5,∑n<=2*10^5\),算式中的数字在\([0,10^9]\)内。

题解

50pts

很容易想到\(O(N^3)\)的区间Dp,维护两个\(mi[l][r],ma[l][r]\),表示在只考虑区间\((l,r)\)的最小/大值。

转移如下:

inline void solve(){
	n=read();
	char s[2];
	for(register int i=1;i<n;++i){
		a[i]=read();
		scanf("%s",s);
		op[i]=(s[0]=='+'?0:1);
	}
	a[n]=read();
	
	for(register int i=0;i<=n;++i)ma[i][i]=mi[i][i]=a[i];
	
	for(register int i=2;i<=n;i++){
		for(register int l=1;l+i-1<=n;++l){
			int r=l+i-1;
			ll now=a[l],Mi=Inf,Ma=-Inf;
			for(register int k=l;k<r;++k){
				if(op[k]){
					MA(Ma,ma[l][k]-mi[k+1][r]);
					MI(Mi,mi[l][k]-ma[k+1][r]);
				}
				else{
					MA(Ma,ma[l][k]+ma[k+1][r]);
					MI(Mi,mi[l][k]+mi[k+1][r]);					
				}
			}
			ma[l][r]=Ma,mi[l][r]=Mi;
		}
	}
	printf("%lld\n",ma[1][n]);
}

70pts

上面那个太无脑了。仔细观察一下,尝试简化问题模型。

对于区间中的某个数字\(a[i]\),假如i前面有\(j\)个左括号\((\)还没匹配,那么,可以根据括号的奇偶性以及i前面的符号,确定此时\(a[i]\)计入总值的正负性。

于是定义状态\(f[i][j]\)表示,现在已经决策完了前i个数字的括号摆放,且有j个左括号还没被匹配,前面i个元素计入总值的和最大为多少。

代码如下,只用枚举\(i,j\),所以时间复杂度为\(O(N^2)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll Inf=1e12;
const int N=1005;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
int n,a[N],op[N];//op=0:+, op=1:-;
ll f[N][N];
inline void MA(ll &x,ll y){if(x<y)x=y;}
inline void MI(ll &x,ll y){if(x>y)x=y;}
inline void solve(){
	n=read();
	char s[2];
	for(register int i=1;i<n;++i){
		a[i]=read();
		scanf("%s",s);
		op[i]=(s[0]=='+'?0:1);
	}
	a[n]=read();
	for(register int i=1;i<=n;i++)for(register int j=0;j<=i;j++)f[i][j]=-Inf;
	f[1][0]=a[1];
	for(register int i=2;i<=n;i++){//当前是第i个数
		for(register int j=0;j<i;j++){//前面还未匹配的括号数
			bool isfu=op[i-1],tmp=j&1;
			int g=1;
			//什么括号都不加
			isfu^=tmp;if(isfu)g=-1;
			MA(f[i][j],f[i-1][j]+g*a[i]);
			//i)
			if(j>0)MA(f[i][j-1],f[i-1][j]+g*a[i]);
			//(i
			if(op[i-1]){
				if(j&1)g=1;
				else g=-1; 
				MA(f[i][j+1],f[i-1][j]+g*a[i]);
			} 
		} 
	}
	//for(register int i=1;i<=n;i++)for(register int j=0;j<i;j++){
	//	cout<<"考虑前"<<i<<"个数字   "<<"现在还有"<<j<<"个左括号未匹配  val="<<f[i][j]<<endl; 
	//}
	printf("%lld\n",f[n][0]);
}
int main(){
	freopen("jerry.in","r",stdin);
	freopen("jerry.out","w",stdout);
	int T=read();
	while(T--)solve();
}

100pts

根据70分的模型,仔细深入一想,其实不必枚举所有\(j\),因为每个数的取值只跟未匹配括号个数的奇偶性有关,而跟具体个数无关。

也就是说,在最优解中,括号的嵌套层数不会超过2

所以只需枚举\(i\),原本的\(j\)那一层变成了常数,相当于\(O(N)\)

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll Inf=1e15;
const int N=2e5+10;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
int n,a[N],op[N];
ll f[N][3];
inline void MA(ll &x,ll y){if(x<y)x=y;}
inline void solve(){
	n=read();
	char s[2];
	for(register int i=1;i<n;++i){
		a[i]=read();
		scanf("%s",s);
		op[i]=(s[0]=='+'?0:1);
	}
	a[n]=read();
	for(register int i=1;i<=n;i++)f[i][0]=f[i][1]=f[i][2]=-Inf;
	f[1][0]=a[1];
	for(register int i=2;i<=n;i++){//当前是第i个数
		for(register int j=0;j<=2;j++){//前面还未匹配的括号数
			bool isfu=op[i-1],tmp=j&1;
			int g=1;
			//什么括号都不加
			isfu^=tmp;if(isfu)g=-1;
			MA(f[i][j],f[i-1][j]+g*a[i]);
			//i)
			if(j>0)MA(f[i][j-1],f[i-1][j]+g*a[i]);
			//(i
			if(j<2&&op[i-1]){
				if(j&1)g=1;
				else g=-1; 
				MA(f[i][j+1],f[i-1][j]+g*a[i]);
			} 
		} 
	}
	printf("%lld\n",f[n][0]);
}
int main(){
	freopen("jerry.in","r",stdin);
	freopen("jerry.out","w",stdout);
	int T=read();
	while(T--)solve();
}
posted @ 2019-10-15 15:41  real_lyb  阅读(16)  评论(0编辑  收藏  举报