C++分治算法之剔除括号

上次是归并排序(Merge Sort),今天,我们还是继续讲分治算法,先引入一道题:

       《道德经》中有云:“万物之始,大道至简,衍化至繁。”所以当某个老师看到一个表示算式的字符串(含四则运算、乘法、括号)中包括有很多多余的括号时,受强迫症的影响,他就会要求你去掉多余的括号,并保持原表达式中变了和运算符的相对位置不变,且与原表达式等价。

       注意,只是要求你去括号,并没有要求你化简表达式!此外,”+”和”-”不会用作正负号。

       【输入样例】

              a+(b+c)

              (a*b)+c/d

              a+b/(c-d)

       【输出样例】

              a+b+c

              a*b+c/d

              a+b/c-d

 

这道题看起来有点复杂,但分析符合”多余括号“的情况只有三种情况:

(1)括号内没有运算符

(2) 括号左侧是加法运算符,右侧是加法或减法运算符

(3)括号左侧是乘法运算符,右侧是乘法或出发运算符

 

 

首先定义运算符的优先级,一般的,将运算符“^”的优先级设为3,运算符“*”和“/”的优先级设为2,运算符“+”和“-”的优先级设为1。

 

       定义一个字符串数组s[ ]保存表达式,与之相对地定义一个整型数组a[ ],标记括是否多余。如图所示:

 

将相匹配的括号对内的表达式看作是一个整体,与括号外的最低运算符进行比较,若括号内的最低运算符优先级大于或等于括号外的最低运算符,则此对括号可剔除,如图所示:

具体操作是,找出式子中最低运算符A,将式子A处分为左右两个子式,再对左右两个子式依次递归下去即可,如图所示:

设递归函数为int cal(intbegin,int end,int prev_min),其中begin表示表达式的起始处,end表示表达式的结尾处,prev_min表示递归前式子的最低运算符级别,则对表达式的递归过程如图所示:

下面附上代码:

//剔除多余括号--------二分法
#include"iostream"
#include"cstdio"
#include"cstdlib" 
#include"cstring"


using namespace std;


int a[1024];
char s[1024];


int cal(int begin,int end,int prev_min)
{
int t;
int min=4;
int min_i;
for(int i=begin;i<=end;i++)
{
switch(s[i])
{
case '^':
if(min>3)
min=3,min_i=i;
break;
case '*':
case '/':
if(min>2)
min=2,min_i=i;
break;
case '+':
case '-':
if(min>1)
min=1,min_i=i;
break;
case '(':
i++;
for(t=1;t!=0;i++)
{
if(s[i]=='(')
t++;
if(s[i]==')')
t--;
}
i--;
break;
};
}

if(min == 4)
{
if(s[begin]=='(' && s[end]==')')
{
t=cal(begin+1,end-1,0);
if(t>=prev_min)
{
a[begin]=a[end]=1;
return t;
}
}
return 4;
}


cal(begin,min_i-1,min);
if(s[min_i]=='+' || s[min_i]=='*')
cal(min_i+1,end,min);
else
cal(min_i+1,end,min+1);
return min;
}


int main()
{
cin>>s;
int sc=strlen(s);
cal(0,sc-1,0);
for(int i=0;i<sc;i++)
{
if(!a[i])
cout<<s[i];
}

cout<<"\n";

return 0;

 

最后再贴一题巩固

T2 聪明的质检员(NOIP 2011提高组 day2

时间限制:1 s   内存限制:128 MB

题目描述

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1到n 逐一编号,每个矿石都有自己的重量 wi 以及价值vi 。检验矿产的流程是:

1 、给定m 个区间[Li,Ri];

2 、选出一个参数 W;

3 、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi:

这批矿产的检验结果Y 为各个区间的检验值之和。即:Y1+Y2...+Ym

若这批矿产的检验结果与所给标准值S 相差太多,就需要再去检验另一批矿产。小T

不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近

标准值S,即使得S-Y 的绝对值最小。请你帮忙求出这个最小值。

输入输出格式

输入格式:

 

输入文件qc.in 。

第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。

接下来的n 行,每行2个整数,中间用空格隔开,第i+1 行表示 i 号矿石的重量 wi 和价值vi。

接下来的m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。

 

输出格式:

 

输出文件名为qc.out。

输出只有一行,包含一个整数,表示所求的最小值。

 

输入输出样例

输入样例#1:

5 3 15

1 5

2 5

3 5

4 5

5 5

1 5

2 4

3 3

输出样例#1:

10

说明

【输入输出样例说明】

当W 选4 的时候,三个区间上检验值分别为 20、5 、0 ,这批矿产的检验结果为 25,此

时与标准值S 相差最小为10。

【数据范围】

对于10% 的数据,有 1 ≤n ,m≤10;

对于30% 的数据,有 1 ≤n ,m≤500;

对于50% 的数据,有 1 ≤n ,m≤5,000;

对于70% 的数据,有 1 ≤n ,m≤10,000;

对于100%的数据,有 1 ≤n ,m≤200,000,0 < wi, vi≤10^6,0< S≤10^12,1 ≤Li ≤Ri ≤n 。

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值