1 条题解

  • 0
    @ 2026-8-31 9:22:47

    这个思路是听我教练讲的,感觉很妙。

    这道题你只需要会表达式求值这道经典题目,其实就可以很轻松地解出来。

    首先以下是表达式求值代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    stack<int> stk;
    stack<char> op;
    char s[1000010];
    map<char,int> mp;
    
    void calc()
    {
    	int b=stk.top();stk.pop();
    	int a=stk.top();stk.pop();
    	int num=0;
    	if(op.top()=='+') num=a+b;
    	else if(op.top()=='-') num=a-b;
    	else if(op.top()=='*') num=a*b;
    	else
    	{
    		if(b==0) puts("error"),exit(0);
    		else num=a/b;
    	}
    	stk.push(num);
    	op.pop();
    	return;
    }
    void solve()
    {
    	int t=strlen(s);
    	for(int i=0;i<t;i++)
    	{
    		if(isdigit(s[i]))
    		{
    			int j=i;
    			int num=0;
    			while(j<t && isdigit(s[j]))
    			{
    				num=num*10+s[j]-'0';
    				j++;
    			}
    			i=j-1;
    			stk.push(num);
    		}
    		else 
    		{
    			if(s[i]=='-')
    			{
    				if(i==0 || s[i-1]=='(')
    				{
    					stk.push(0),op.push('-');
    				}
    			}
    			if(s[i]=='(') op.push('(');
    			if(s[i]==')')
    			{
    				while(!op.empty() && op.top()!='(') 
    				{
    					calc();
    				}
    				if(!op.empty()) op.pop();
    			}
    			if(s[i]!='(' && s[i]!=')')
    			{
    				while(!op.empty() && mp[op.top()]>=mp[s[i]]) 
    				{
    					calc();
    				}
    				op.push(s[i]);
    			}
    		}
    	}
    	while(!op.empty() && op.top()!='(') 
    	{
    		calc();
    		}
    	printf("%s=%d",s,stk.top());
    }
    int main()
    {
    	scanf("%s",s);
    	mp['+']=1;
    	mp['-']=1;
    	mp['*']=2;//mp表示优先级的大小。
    	mp['/']=2;
    	solve();
    }
    

    首先我们考虑用以上代码算出此题中表达式的结果。

    很简单,首先就是把 map 的优先级符号改了,然后把 calc 函数里面的内容改了,剩下的细节就不多说了。

    然后下面就是重点了,如何算出短路的个数

    以下拿位运算符号或来举例。

    首先,假设现在有一个符号或:

    我们已经通过表达式求值算出了他左右两边的值,假设为 aabb。再假设我们已经算出左右两边或的短路,与的短路,我们分别假设为 x1x1y1y1x2x2y2y2

    那么如下图:

    如果 aa 为为 11,这里是不是就构成一个短路了?

    那么其实,此表达式的短路数就是 aa 的短路或次数加一(与和或分开来算),与运算短路与 aa 的短路次数相同。因为题目要求,所以我们 bb 算出来的短路次数就直接作废。多加的一就是现在或的短路。

    而如果 aa00,那么不构成短路,aabb 的值都有效。所以这个表达式的短路数就是 aa 的短路数加 bb 的短路数。

    而位运算与其实也可以像或一样分类讨论运算。

    这便是思路的核心。

    可能有人会问,怎么实现?

    其实我们只需要套个表达式求值模板跑一遍就好了。

    因为表达式求值的模板可以算出优先级高的部分,然后慢慢算出低的部分,每次运算时,都是拿栈顶的两个元素计算,我们只需要按照上述内容,用栈顶的两个元素进行短路合并就好了。

    可能我语文水平不行,上面讲的很多内容大家会看不懂,但是你们看完代码一定会茅塞顿开的。

    #include <bits/stdc++.h>
    using namespace std;
    
    stack<int> stk;
    stack<pair<int,int>> duan;
    stack<char> op;
    char s[1000010];
    map<char,int> mp;
    
    void merge_duan(pair<int,int> aa,pair<int,int> bb,int a,int b,char op)
    {
    	pair<int,int> c;
    	if(a==1 && op=='|')
    	{
    		c.first=aa.first+1;//跟上述内容相符。
    		c.second=aa.second;
    	
    	}
    	else if(a==0 && op=='&')
    	{
    		c=aa;
    		c.second++;//如果a为0并且符号为与,那么构成了一个短路,为a的与的短路数加一,或的短路不变,b的短路数直接作废。
    	}
    	else
    	{
    		c.first=aa.first+bb.first;
    		c.second=aa.second+bb.second;
    	}
       duan.push(c);//把算好的短路次数放回去,准备下一次运算。
    }
    void calc()
    {
    	int b=stk.top();stk.pop();
    	int a=stk.top();stk.pop();//表达式求值模板。
    	int num=0;
    	pair<int,int> bb=duan.top();duan.pop();
    	pair<int,int> aa=duan.top();duan.pop();//跟表达式求值一样,每次从栈顶弹出两个元素进行运算。
    	merge_duan(aa,bb,a,b,op.top());//合并。
    	if(op.top()=='|') num=a|b;
    	else if(op.top()=='&') num=a&b;
    	stk.push(num);
    	op.pop();
    	return;
    }
    void solve()
    {
    	int t=strlen(s);
    	for(int i=0;i<t;i++)
    	{
    		if(isdigit(s[i])) stk.push(s[i]-'0'),duan.push({0,0});//如果遇到数就直接放入栈中,并且为这个数创建一个新的空间,代表他目前算出的短路次数,第一个代表或,第二个代表与。
    		else 
    		{
    			if(s[i]=='(') op.push('(');
    			if(s[i]==')')
    			{
    				while(!op.empty() && op.top()!='(') calc();//表达式求值模板。
    				if(!op.empty()) op.pop();
    			}
    			if(s[i]!='(' && s[i]!=')')
    			{
    				while(!op.empty() && mp[op.top()]>=mp[s[i]]) calc();
    				op.push(s[i]);
    			}
    		}
    	}
    	while(!op.empty()) calc();
    	printf("%d\n",stk.top());
    	printf("%d %d",duan.top().second,duan.top().first);
    }
    int main()
    {
    	scanf("%s",s);
    	mp['|']=1;
    	mp['&']=2;//优先级。
    	solve();
    }
    

    这道题其实感觉没有绿题的难度,就是跑了一遍表达式求值。

    • 1

    信息

    ID
    1980
    时间
    1000ms
    内存
    512MiB
    难度
    8
    标签
    递交数
    23
    已通过
    4
    上传者