【BZOJ1561】[JSOI2009]去括号【表达式相关】

【题目链接】

一开始按照判断括号是否可以去掉的方法写,对着数据调了无数个小时,也就只过了一个点。

后来问Claris(%%%%)要了代码,发现原来还可以这么搞。


先用栈处理出每个左或右括号对应的另一个右或左括号的下标。


然后求每个运算符的优先级。

具体是这样的:

(1)定义一个临时变量j,表示当前位置的优先级,然后遍历表达式,初始j为1。

(2)进入一个括号j加2。(即遇到左括号)

(3)+和-的优先级直接赋j。

(4)*和/的优先级赋j+1。

(5)退出一个括号j减2。(即遇到右括号)

有一点要注意就是遇到 ((sth)) 这种情况,这种情况下优先级只加一次2,而不是加4。这种情况的判断是用预处理出来的括号位置搞的,不懂看代码就懂了。


预处理所有的优先级之后,就可以去括号了。

从1开始dfs。(1这个位置本来是不一定有括号的,我们要人为的给表达式两边再加一对括号,这样1这个位置就有括号了,方便处理)

每次dfs,先遍历串,处理之后的括号,保证正确性,同时还可以得到右括号的位置。


然后获取这两个括号左右位置的运算符的优先级,取最大的。

看括号内部是否有+或-号,这个不是直接看字符串,而是用优先级的大小关系判断。

如果有,那么不能去掉当前括号,返回。

如果没有,那么可以去掉,然后变号。


变号时,如果前面是减号,那么+变-,-变+。

如果前面是除号,那么*变/,/变*。


看代码吧。


%一发Claris

/* Pigonometry */
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 305;

int n, pri[maxn], lbra[maxn], rket[maxn];
char str[maxn];

int sta[maxn];

inline int dfs(int l) {
	int r;
	for(r = l + 1; str[r] != ')'; r++)
		if(str[r] == '(') r = dfs(r);

	int k = max(pri[l - 1], pri[r + 1]), pos;
	for(pos = l + 1; pos < r; pos++) if(pri[pos] == k + 1) break;
	if(pos < r) return r;

	str[l] = str[r] = '$';

	int cnt = 0;
	if(str[l - 1] == '-')
		for(int i = l + 1; i < r; i++) {
			if(str[i] == '(') cnt++;
			else if(str[i] == ')') cnt--;
			if(!cnt)
				if(str[i] == '-') str[i] = '+';
				else if(str[i] == '+') str[i] = '-';
		}
	if(str[l - 1] == '/')
		for(int i = l + 1; i < r; i++) {
			if(str[i] == '(') cnt++;
			else if(str[i] == ')') cnt--;
			if(!cnt)
				if(str[i] == '*') str[i] = '/';
				else if(str[i] == '/') str[i] = '*';
		}
	return r;
}
	

inline void solve() {
	memset(pri, 0, sizeof(pri));
	scanf("%s", str + 2); n = strlen(str + 2);
	str[1] = '('; str[n + 2] = ')';

	int top = 0;
	for(int i = 1; i <= n; i++)
		if(str[i] == '(') sta[++top] = i;
		else if(str[i] == ')') rket[sta[top]] = i, lbra[i] = sta[top--];

	pri[0] = pri[n + 1] = 1;
	for(int i = 1, j = 1; i <= n; i++)
		if(str[i] == '(' && (str[i - 1] != '(' || str[rket[i] + 1] != ')')) j += 2;
		else if(str[i] == ')' && (str[i + 1] != ')' || str[lbra[i] - 1] != '(')) j -= 2;
		else if(str[i] == '+' || str[i] == '-') pri[i] = j;
		else if(str[i] == '*' || str[i] == '/') pri[i] = j + 1;

	dfs(1);
	
	for(int i = 2; i <= n + 1; i++)
		if(str[i] != '$') printf("%c", str[i]);
	printf("\n");
}

int main() {
	int T; scanf("%d", &T);
	while(T--) solve();
	return 0;
}


你可能感兴趣的:(【BZOJ1561】[JSOI2009]去括号【表达式相关】)