10.02区间DP
Description
某正教授级特级教师曾经热爱研究数学。数学中的表达式有前缀、后缀、中缀等写法。中缀是最常见的,如1+2,9*(1-4)等,前缀表达式符号写在 2个数之前,如刚刚两个表达式应为+12,*9-14(本题中假设所有操作数都是一位数,于是不用空格分隔,下同)。后缀表达式中运算符写在两个数之后,如12+和 914-*。前缀和后缀表达式不需要括号。
特教受此启发,自己写出了一种“混合表达式”,即将前缀、中缀和后缀的规则混合书写一个表达式,表达式中仅出现操作数和加减运算符(注意本题中加减号不能表示正负号)。每一步运算可以任意选取表达式中一个加减运算符,按照前缀、中缀或后缀的任意一种规则运算(前提是运算合法)。经过若干步运算后最后剩下的数即为最后结果,运算的中间过程及结果中可以出现多位数以及负数的情况。比如+2-31,选取中间的减号,可以按中缀规则算得 +(-1)1(2-3=-1)也可以按前缀规则算得+22( 3-1= 2 )。
特教发现这样的混合表达式运算结果不唯一,于是他写出了一个混合表达式,要求你计算出它所有可能的结果种数(用不同方式恰好算出相同的答案也算同一种结果)。
特教受此启发,自己写出了一种“混合表达式”,即将前缀、中缀和后缀的规则混合书写一个表达式,表达式中仅出现操作数和加减运算符(注意本题中加减号不能表示正负号)。每一步运算可以任意选取表达式中一个加减运算符,按照前缀、中缀或后缀的任意一种规则运算(前提是运算合法)。经过若干步运算后最后剩下的数即为最后结果,运算的中间过程及结果中可以出现多位数以及负数的情况。比如+2-31,选取中间的减号,可以按中缀规则算得 +(-1)1(2-3=-1)也可以按前缀规则算得+22( 3-1= 2 )。
特教发现这样的混合表达式运算结果不唯一,于是他写出了一个混合表达式,要求你计算出它所有可能的结果种数(用不同方式恰好算出相同的答案也算同一种结果)。
Input
仅一行,包含一个仅含有数字和加减号的字符串,表示一个混合表达式。保证该表达式合法,即至少能计算出一种结果。
Output
仅一行,输出一个数 N,表示可能的结果种数。
Sample Input
+54+-82Sample Output
2
Hint
样例输入2:
1-9-+73+6-2
样例输出2:
8
【样例 1 说明】
以下为 2 种可能的情况,可能的运算方式没有全部列出。
(1)+54+-82=+54+6=9+6=15
(2)+54+-82=+9-82=+12=3
【数据范围】
对于 50%的数据,表达式字符串的长度不超过 10;
对于 100%的数据,表达式字符串的长度不超过 80。
1-9-+73+6-2
样例输出2:
8
【样例 1 说明】
以下为 2 种可能的情况,可能的运算方式没有全部列出。
(1)+54+-82=+54+6=9+6=15
(2)+54+-82=+9-82=+12=3
【数据范围】
对于 50%的数据,表达式字符串的长度不超过 10;
对于 100%的数据,表达式字符串的长度不超过 80。
乍看一下给人无从下手的感觉,仔细思考一下可以提取出几个关键点,1.通过不同的计算方法能够计算出多少种不同的值?存在性。2.一个大区间最终所得到的值是由两个小区间的值通过运算符号连接起来的?子结构。综合这两点我们想到了DP,区间合并加存在性的DP,用vector f[i][j]存贮合并i到j区间后所能得到的所有数,不断枚举长度不断增加的区间并枚举其合并方式与断点,将得到的数放入f[i][j]中,那么最终的答案就是f[1][len].size();
实际上就是石子合并的思想,区间可以进行操作
注意点(debug时调试的错误):
1.计算时查存在性的时候容易的下标是负数
2.i,j容易误用,差错要用这个东西
3.条件比如该状况下的用作运算的符号位不能当做分割点
此题特点,前缀表达式后缀表达式中缀表达式以符号分割满足区间DP条件
我tm写成大模拟还写挂了
code:
1 //i,j误用。。。 2 //isdigit isupper islower isalpha 3 #include<iostream> 4 #include<cstdio> 5 #include<string> 6 #include<algorithm> 7 #include<cstring> 8 #include<vector> 9 using namespace std; 10 int exist[100][100][1200]; 11 vector<int>dp[100][100]; 12 void add(vector<int>a,vector<int>b,int l,int r,int flag){ 13 if(a.size()==0||b.size()==0)return; 14 for(int i=0;i<a.size();i++){ 15 for(int j=0;j<b.size();j++){ 16 if(exist[l][r][a[i]+flag*b[j]+500]==0){ 17 exist[l][r][a[i]+flag*b[j]+500]=1; 18 dp[l][r].push_back(a[i]+flag*b[j]); 19 } 20 } 21 } 22 } 23 int main(){ 24 char s[100]; 25 scanf("%s",s+1); 26 int len=strlen(s+1); 27 for(int i=1;i<=len;i++){ 28 if(isdigit(s[i])){ 29 dp[i][i].push_back(s[i]-'0'); 30 exist[i][i][s[i]-'0']=1; 31 } 32 } 33 for(int k=3;k<=len;k+=2){ 34 for(int i=1;i+k-1<=len;i++){ 35 int j=i+k-1; 36 int num_number=0,num_oper=0; 37 for(int l=i;l<=j;l++){ 38 if(isdigit(s[l]))num_number++; 39 else num_oper++; 40 } 41 if(num_oper!=num_number-1)continue; 42 for(int l=i;l<=j;l++){ 43 if(s[i]=='+'&&l>i)add(dp[i+1][l],dp[l+1][j],i,j,1); 44 if(s[i]=='-'&&l>i)add(dp[i+1][l],dp[l+1][j],i,j,-1); 45 if(s[j]=='+'&&l<j)add(dp[i][l],dp[l+1][j-1],i,j,1); 46 if(s[j]=='-'&&l<j)add(dp[i][l],dp[l+1][j-1],i,j,-1); 47 if(s[l]=='+')add(dp[i][l-1],dp[l+1][j],i,j,1); 48 if(s[l]=='-')add(dp[i][l-1],dp[l+1][j],i,j,-1); 49 } 50 } 51 } 52 cout<<dp[1][len].size(); 53 return 0; 54 }
over