[AcWing蓝桥杯]之数论^ ^(C++题解)

目录

数论的一些模板:

(1)朴素筛法求素数

(2)线性筛--->>时间复杂度为O(N)(所以一般情况下都使用这个)

(3)分解数字为质因数

(4)约数的个数

(5)约数之和

(6)欧几里得算法(辗转相除)

(7)扩展欧几里得算法

公约数(欧几里得+更相减损)

等差数列

最大比例(等比数列)

线性同余方程+扩展欧几里得

         五指山

C循环

筛质数+分解质因数+组合计数

X的因子链

聪明的燕姿

正则问题


数论的一些模板:

这是我之前写的一些模板:详细的步骤过程在里面,这里只是核心步骤

数论之埃氏筛,线性筛,欧几里得算法,欧拉函数,欧拉定理,费马定理,快速幂,扩展欧几里得算法,线性同余方程,中国剩余定理(acwing模板篇)_lihua777的博客-CSDN博客_欧几里得定理acwing(8)https://blog.csdn.net/lihua777/article/details/122961428?spm=1001.2014.3001.5501

 根据蓝桥杯重点考察的回顾一下重点的数论算法

(1)朴素筛法求素数

核心:

内循环遍历完质因子的倍数,并将其标记为合数

#include<iostream>
using namespace std;
const int N = 1000010;
int primes[N], cnt;
bool st[N];
void get_primes(int n) 
{
	for (int i = 2; i <= n; i++)
	{
		if (!st[i]) 
		{
			primes[cnt++] = i;
			for (int j = i + i; j <= n; j += i) st[j] = 1;//间隔
		}
	}
}
int main() 
{
	int n;
	cin >> n;
	get_primes(n);
	cout << cnt;
}

(2)线性筛--->>时间复杂度为O(N)(所以一般情况下都使用这个)

核心:

思路与上面的朴素筛法差不多,都是标记倍数,不过线性筛每次时标记离自己最近那个倍数

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1000010;
int primes[N], cnt;
bool st[N];
void get_primes(int n)//12
{
	for (int i = 2; i <= n; i++) 
	{
		if (!st[i])//如果没有被标记过:证明时素数
		{
			primes[cnt++] = i;//primes[0]=2  primes[1]=3
		}
		for (int j = 0; primes[j] <= n / i; j++)
		{//st[4]=1  st[6]=1 st[9]=1
			st[primes[j] * i] = 1;//遍历素数数组,标记素数的i倍为true
			if (i % primes[j] == 0) break;//2%2==0  3%3==0 意思时遍历到最后primes数组的最后一个数了 break
		}
	}
}
int main() 
{
	int n;
	cin >> n;
	get_primes(n);
	cout << cnt;
 
	return 0;
}

(3)分解数字为质因数

核心:

公理:每个数字都可被拆分为质因数^指数的乘积形式(如下图所示)

 

#include<iostream>
using namespace std;
void divide(int n) 
{
	for (int i = 2; i <= n / i; i++) 
	{
		if (n % i == 0) 
		{//i一定是质数,因为此时2到i-1的质因子已经被除干净了 
			int s = 0;//计算次数 
			while (n % i == 0) 
			{
				n /= i;//i为什么是质数的原因
				s++;
			}
			printf("%d %d\n", i, s);
		}
	}
	if (n > 1) printf("%d %d\n", n, 1);//特判:当有一个比较大的质因子时
	cout << endl;
 
}
int main() 
{
	int n;
	cin >> n;
	while (n--) 
	{
		int x;
		cin >> x;
		divide(x);
	}
	return 0;
}

(4)约数的个数

核心:如下图

#include<iostream>
#include<unordered_map>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
 
int main() 
{
	int n;
	cin >> n;
	ll ans = 1;//因为要连乘,所以初始化为1
	unordered_map<int, int> hash;
	while (n--) 
	{
		int x;
		cin >> x;
		for (int i = 2; i <= x / i; i++) 
		{
			while (x % i == 0) 
			{
				x /= i;//分解质因数
				hash[i]++;//质因数的指数++(次数即指数)
			}
		}
		if (x > 1) hash[x]++;//保留最后一个质因数
	}
	for (auto &i : hash) ans = ans * (i.second + 1) % mod;//约数公式
	cout << ans;
    
    return 0;
}

(5)约数之和

核心:如下图

#include <iostream>
#include <algorithm>
#include <unordered_map>
 
using namespace std;
typedef long long LL;
const int N = 110, mod = 1e9 + 7;
 
int main()
{
    int n;
    cin >> n;
    unordered_map<int, int> primes;
 
    while (n--)
    {
        int x;
        cin >> x;
 
        for (int i = 2; i <= x / i; i++)
            while (x % i == 0)
            {
                x /= i;
                primes[i] ++;
            }
 
        if (x > 1) primes[x] ++;
    }
 
    LL res = 1;
 
    // ------------------约数之和公式-------------------------
    for (auto p : primes)
    {
        LL a = p.first, b = p.second;
        LL t = 1;
        while (b--) t = (t * a + 1) % mod; // while (b -- ) t = (t * a + 1) % mod; 是什么意思?
        res = res * t % mod;
    }
 
    cout << res << endl;
 
    return 0;
}

(6)欧几里得算法(辗转相除)

核心:

gcd(a,b)=gcd(b,a%b)

(1)当b==0时,0与任意一个数的最大公约数都是它本身

(2)当b!=0时就是gcd(a,b)=gcd(b,a%b)

#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a, int b)
{
	return b ? gcd(b, a % b) : a;
}
int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int a, b;
		cin >> a >> b;
		cout << gcd(a, b) << endl;
	}
 
	return 0;
}

(7)扩展欧几里得算法

作用:求出最大公约数的同时,根据裴蜀定理求出满足ax+by=gcd(a,b)(不一定是最大公约数)

        的x0,y0,然后根据公式求出多组满足的解x,y

#include<iostream>
#include<algorithm>
using namespace std;
 
void exgcd(int a, int b, int& x, int& y)
{
	if (!b)
	{
		x = 1, y = 0;
		return;
	}
	exgcd(b, a % b, y, x);
	y -= a / b * x;
	return;
}
 
int main()
{
	int n;
	cin >> n;
 
	while (n--)
	{
		int a, b;
		cin >> a >> b;
		int x, y;
		exgcd(a, b, x, y);
		printf("%d %d\n", x, y);
	}
 
	return 0;
}

复习的内容就到这里了,现在要开始刷题了^  ^


公约数(欧几里得+更相减损)


等差数列

1246. 等差数列 - AcWing题库

概述:给出一段序列,求出满足该段序列的最短等差数列的长度

初始思路:

(1)既然要求的是最短的等差数列的长度,那么就要要求公差比较大,那么这个序列就短

(2)那么问题来了:公差怎么取???

如果公差取得比较大,那么很有可能就不满足等差序列这个条件了:比如下面这个例子

2  4   8  (如果公差取4,那么2->4,就不满足公差为4,这个性质了->不满足等差数列)

综上所述:我们要求的是一个数列中满足题意的最小的公差即可,

那么现在的问题就转为了:如何求上述的特殊公差?

根据数据范围我们可以直到这个算法不能写O(N^2)

2 ≤ N ≤ 100000(两层循环会爆)
0 ≤ Ai≤ 10^9

 

 算法优化:

优化:首先遍历一遍数组是跑不了了,要做的优化就是求最小的公差那部分

有没有一种可能,可以用另外一种方式求公差,那就是欧几里得算法求公约数

为什么可以这样写???为说明会联想到公约数???

因为在等差数列中,首项为a1,剩下的数均可表示为a1+nd,那么是不是只需要同时减去a[0],也就是首项,那么剩下的数均可表示为nd,那么n不同,d一定相同,那么是不是就完美满足了最大公约数这个性质了^ ^

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int a[N];

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    sort(a, a + n);//排序是为了让首项为a[0]

    int d = 0;//0与任何数的最大公约数都是它的本身
    for (int i = 1; i < n; i++) d = gcd(d, a[i] - a[0]);//减去首项a1

    if (!d) printf("%d\n", n);//如果公约数为0.那么就证明这时一个常数数列
    else printf("%d\n", (a[n - 1] - a[0]) / d + 1);//上面图片的公式

    return 0;
}

最大比例(等比数列)

1223. 最大比例 - AcWing题库

                                                         图片来源:AcWing视频

 (1)对于每一个等比数列,都可以写为首项*公比的形式

那么对于本题而言,要求的是分数形式的最大公比,那么就型需要p/q,来代替r,作为公比

其中p/q,对于其中的p,q而言,这两个数需要两两互质,这样它们才是最简分式

 

 (2)那么为了简介,将上式写为b数列的形式,那么,在b数列中

注意此时先预处理进行排序,那么b0,就是最小的数,将它作为首项

b0后的每一每一项除以b0,就等于(p/q)的alpha次方

 

 (3)对于让(p/q)最简有两个条件必须满足:

<1>,p,q互质(最大公约数为1)

<2>,p,q不能再拆分为次幂形式

        比如说4/9,已经满足条件<1>了,但是它可以写为(2/3)^2次方,所以此时仍不是最简

 

 

 (4)回到题目所求:最大比例,那么其实也就是最大公比

也就是上图所说的K最大即可,但是也要注意满足该数列为等比数列

那么下面:

对于一个相邻的(p/q),(注意因为已经排好序了,那么后面的元素一定是比前面的大的)

下图中:alphai是指对于k的后一个数,那么就有:

alphai=k*s,那么就很明显了,对于alphai而言s,是它和k的公约数

 

 

 (5)辗转相减法(更相减损数):

来源:AcWing 1223. 最大比例 - AcWing

/*更相减损术:第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2的积与第二步中等数的乘积就是所求的最大公约数。*/

//更相减损术总用较大的减去较小的
/*例子:
    98-63=35
    63-35=28
    35-28=7
    28-7=21
    21-7=14
    14-7=7
所以,98和63的最大公约数等于7。*/

//我们这里要用更相减损术的是指数,所以要让(p/q)^x1,(p/q)^x2,...,(p/q)^x(N-1),两两计算,互除,除到结果为1,即x1=x2,此时幂次为0,结果为1
//把分子分母分别去算,结果是相同的因为,分子分母的幂次是相同

 

 

#include<iostream>
#include<algorithm>


using namespace std;

typedef long long LL;

const int N = 110;

int n;
LL x[N];//存入输入的数据
LL a[N], b[N];//分别表示分子p和分母q

LL gcd(LL a, LL b)//辗转相除
{
    return b ? gcd(b, a % b) : a;
}

LL gcd_sub(LL a, LL b)//辗转相减
{
    if (b > a)swap(a, b);
    if (b == 1)return a;
    return gcd_sub(b, a / b);
}
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)cin >> x[i];//读入

    sort(x, x + n);

    LL dd = 0;//0与任何数的最大公约数都是其本身
    int cnt = 0;
    for (int i = 1; i < n; i++)//预处理得到b数组
    {
        if (x[i] != x[i - 1])//去重
        {//对于第一项而言:相当于取出了首项
         //对于第一项之后的每一项而言:相当于取出了最大公约数
            dd = gcd(x[i], x[0]);//最大公约数
            a[cnt] = x[i] / dd;//分子的最简==原来的值/(原来的值与上一项的最大公约数)
            b[cnt] = x[0] / dd;//第一项的最简形式作为分母
            cnt++;
        }
    }

    LL up = a[0], down = b[0];//up分子 down分母
    for (int i = 1; i < cnt; i++)//分开求分子分母的指数最大公约数
    {
        up = gcd_sub(up, a[i]);//分子
        down = gcd_sub(down, b[i]);//分母
    }

    cout << up << "/" << down;

    return 0;
}

线性同余方程+扩展欧几里得


五指山

1299. 五指山 - AcWing题库

扩展欧几里得定理:指对于任意正整数对(a,b),一定存在非零整数x和y,使得 ax+by=gcd(a,b)

如果我们求出x和y的一对,我们记为x0和y0     那么其他的x和y可以通过x0,y0表示:

那么其他的x和y可以表示为:x=x0+kb,y=y0-k*a

回到本题:

给出初始位置,目标位置,圆环的长度,一次能走多少步,让我们求最少走多少步可以到达目标位置

标注字母:

        将初始位置标记为:x,将目标位置标记为:y

        将步长标记为:d,将圆环的长度标记为:a

        将步数标记为:b,将走过的环数标记为:n

那么就有这样的方程:x+bd = y+an         整理得:-an+bd = y-x

分析式子:题目要求的是最小的步数,也就是式子中的b

式子中一共有2个未知数,也就是步数:b  和   环数:n

分析到这里,应该马上与裴蜀定理联想起来:

因此我们只需要判断y-x是否为gcd(n,d)的整数倍就能判断是否有解了

若有解我们利用扩展欧几里得定理就可以求得-an+bd = gcd(n,d)中的a和b

根据一组解,从而可以推出多组解,其他解就可以表示出来了

那么,要求的是最小步数,所以只要记录最小的b即可,但是要注意b只能为正数(方向不可相反)

求一下b = b0+k*(n/gcd(n,d))的最小值即可
minb = b0%(n/gcd(n,d));

证明过程需要回看 = =

 

 上述来自:

AcWing 1299. 五指山(梳理) - AcWing

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

typedef long long LL;

LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(!b){
        x = 1,y = 0;
        return a;
    }
    int d = exgcd(b,a%b,y,x);
    y -= a/b*x;
    return d;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        LL n,d,x,y,a,b;
        scanf("%lld%lld%lld%lld",&n,&d,&x,&y);

        int gcd = exgcd(n,d,a,b);
        if((y-x)%gcd)
        {
            printf("Impossible\n");
        }
        else
        {
            b*=(y-x)/gcd;//公式
            n/=gcd;//公式
            printf("%lld\n",(b%n+n)%n);//保证为正数
        }
    }
    return 0;
}

C循环

1301. C 循环 - AcWing题库

题解来自:

AcWing 1301. 24 C循环 - AcWing

k 位存储系统:
所有数值保留最后k位

注意:都是以二进制进行存储的

(A + xC)mod 2^k = B 循环就结束了

//用拓展欧几里得算法, 形如ax + by = d

A + xC - y2^k = B
xC - y2^k = B - A

形如ax + by = d
当求出一组解x0, y0, 那么所有解都可以用x0, y0表示

此时就与上题是一模一样的了

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y)  //拓展欧几里得算法 ax + by = d
{
    if (b == 0)  //边界情况
    {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);  //y*b + x(a mod b) = d;
    y -= a / b * x;  //公式推系数(下方图一)
    return d;
}

int main()
{
    LL a, b, c, k;

    while (cin >> a >> b >> c >> k, a || b || c || k)  //逗号表达式
    {
        LL x, y;  //系数  公式:x*C - y2^k = B - A
        LL z = 1ll << k;  //为了方便,公式:x*C - y*z = B - A
        LL d = exgcd(c, z, x, y); 
        if ((b - a) % d)  //有没有解取决于B - A是否能整除d
            cout << "FOREVER" << endl;  //求余不为0,一定无解
        else  //否则一定有解
        {
            x *= (b - a) / d;  //因为等式右边原本是d,现在是(B - A), 所以等式右边x, y乘上(B-A)/d
            z /= d;  //根据公式,x = x0 + k*(b/d),这里的b就是z,此时所有的解x和x0的差,都是z的倍数
            cout << (x % z + z) % z << endl;  //x的最小非负整数解,就是x0 mod z, c++中取模可能是负数
        }
    }

    return 0;
}

筛质数+分解质因数+组合计数


X的因子链

1295. X的因子链 - AcWing题库

(1)公理:每一个数都可以被分解为多个质数相乘的形式

其中题目要求的是:满足的子序列的最大长度和满足最长的子序列的个数

那么由算术基本定理可知:

最大长度即:质因子出现的次数总和

 

 (2)观察下图的右上角:

一个正整数可以被拆分为如下形式:

那么满足的最长序列长度已经知道了:就是2+3+1(次数之和即可)

那么,现在的另外一个问题是:如何求满足的序列个数,也就是2个2,3个3,1个5,可以组合成多少种方式???

这里就用到了组合数学:

正确的解法是:(次数的总和)!/(分别的次数!)  如下图左上角

 线性筛法:

 

 根据以上的综合分析可知:

对于线性筛:我们要开primes数组来存放质数,要开st数组来判断是否为质数,要用cnt来记录质数的个数

对于题目本身而言:我们要记录该数的最小质数,也就是要开min_p数组来存放最小质因子

要用sum数组来记录每个质因子出现的次数:目的是为了使用上述的组合数求个数和长度

#include<cstdio>

typedef long long ll;

const int N = (1 << 20) + 10;

int primes[N];// 存质数
int min_p[N]; //存最小质因子
int cnt;
bool st[N]; // 表示对应元素是否被筛过
int sum[N]; //记录每个质因子出现的次数

// 线性筛法(欧拉筛法)
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            min_p[i] = i;
            primes[++cnt] = i;
        }
        for (int j = 1; primes[j] * i <= n; j++) {
            int t = primes[j] * i;
            st[t] = true; //标记合数
            min_p[t] = primes[j];
            if (i % primes[j] == 0) {
                //如果i是前面某个素数的倍数时, 说明i以后会由某个更大的数乘这个小素数筛去
                //同理, 之后的筛数也是没有必要的, 因此在这个时候, 就可以跳出循环了
                break;
            }
        }
    }
}


int main() {
    get_primes(N);
    int x;
    while (scanf("%d", &x) != EOF) {
        //tol用于记录最大长度,k表示第i个质因子的下标, 从0开始
        int k = 0, tol = 0;

        // 依次处理各个质因子, 求出对应质因子出现的次数
        while (x > 1) {
            int p = min_p[x]; // 通过while, 依次取出最小质因子
            sum[k] = 0;//初始化为0

            //处理当前质因子, 求其出现的次数
            while (x % p == 0)
            {
                sum[k]++;//质数的次数++
                tol++;//质数出现1次,长度+1
                x /= p;//除以该质数,相当于筛掉
            }
            k++; // 准备处理下一个质因子
            /*
             例:
                x=12 --> 3*2^2
                p = 2
                sum[1]=1
                x=6

                sum[1]=2
                x=3
                ****************
                p=3
                sum[2]=1
                x=1 --> 结束
             */
        }

        //求所有质因子出现总次数的全排列
        ll res = 1;
        for (int i = 2; i <= tol; i++) {
            res *= i; //分子
        }
        //去除各个质因子重复出现的次数
        for (int i = 0; i < k; i++) {
            for (int j = 1; j <= sum[i]; j++) {
                res /= j;//分母
            }
        }
        printf("%d %lld\n", tol, res); //输出最长序列的长度, 以及满足最大长度的序列的个数
    }
    return 0;
}

作者:Bug-Free
链接:https://www.acwing.com/solution/content/23671/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

聪明的燕姿

1296. 聪明的燕姿 - AcWing题库

(1)算术基本定理

那么题目要求的就是有多少个数字的约数之和满足给定的数字,并按顺序输出这些数字

即套用画线公式

由数据分析知道:满足该式子的数会很少,所以可以用暴力搜索

 (2)首先想出框架:

<1>外层for循环,枚举质数

<2>内层for循环,枚举该质数的从0到alpha的次幂之和

判断条件:如果这个和可以被S(也就是给定的数字所整除),那么就向下搜索

搜索边界:如果S==1时就停止搜索(因为S==1)即这条路径上的所有节点都能整除S,

证明这条路径的组合,满足了上述的公式,此时return 

因为是满足条件才向下搜索,所以不用担心其他的情况导致不能返回

(3)剪枝优化:

<1>当枚举的质数的平方数已经大于了S,那么可以提前返回,所以第一层循环才可以这么写

因为这里可以认为成前面的数的 最高次幂*该质数的乘积  类似与 目前遍历到的质数的平方数

 <2>if判断也是如此,如果S=(1+pi)这种情况

那么我们需要同时判断S-1是否大于上一个质数 且  S-1是否质数

因为我们要保证质数是从小到大枚举的,只有剩下的S-1是大于上一层的质数的时候,(1+S)才有可能成为最初的那个S的一个因子

因为我们要表示S’ = (1+ 2 + 2^2+…)(1 + 3 + 3^2 + …)…(1+S)
越往后枚举对应的那个Pi也越大,所以S-1一定要大于上一层的质数才能满足条件

                                                                     <2>转自:AcWing 1296. 聪明的燕姿 - AcWing

 

 

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 50000;//N = sqrt(2e9)
bool st[N];
int primes[N],cnt = 0;//线性筛
int S,res[N],len;

void get_primes(int n)//线性筛
{
    for(int i = 2;i <= n;i++)
    {
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0;primes[j]*i <= n;j++)
        {
            st[primes[j]*i] = true;
            if(i % primes[j] == 0) break;
        }
    }
}

int is_prime(int n)//判断是否为质数
{
    if(n < N) return !st[n];//没有被筛过说明就是质数,返回true
    for(int i = 0;primes[i] <= n / primes[i];i++)
    {
        if(n % primes[i] == 0) return false;
    }
    return true;
}
//last参数:表示上一个枚举的质数是谁
//product参数:当前进行到哪一个括号里面的最高次项Pi^ai的乘积和
//S参数:代表着从一开始的S除以(1+pk+pk^2+…+pk^ak)后剩余的乘积

void dfs(int last,int product,int S)//last表示上一个用的质数的下标是什么,product当前最高次项的结果,S表示每次处理后剩余多少
{
    if(S == 1)//当S和j两个数相等时
    {
        res[len++] = product;
        return ;//记录返回
    }
    //比如20 = 2^2 * 5
    //N = P1^a1 * P2^a2 * ... * Pn^an
    //S = (1+p1+p1^2+...+p1^a1)(1+p2+p2^2+...+p2^a2)...(1+pn+pn^2+...+pn^an)
    //42 = (1 + 2 + 2^2)*(1 + 5),其中2^2和5就分别是最高次项p1^2*p2^1
    if(S-1 > ((last < 0) ? 0 : primes[last]) && is_prime(S-1))//剪枝
    {
        /*因为我们要保证质数是从小到大枚举的,只有剩下的S-1是大于上一层的质数的时候,
		(1+S)才有可能成为最初的那个S的一个因子
		*/
        res[len++] = product * (S-1);
    }
    for(int i = last+1;primes[i] <= S / primes[i];i++)
    {
        int p = primes[i];
        for(int j = 1+p,t = p;j <= S;t *= p,j += t)//60行对应的公式
        {
            if(S % j == 0)/*即S % (1+pk+pk^2+…+pk^ak) == 0然后S /= (1+pk+pk^2+…+pk^ak),再dfs到下一层*/
            {
                dfs(i,product*t,S/j);//第三个参数(每次都要除以)
            }
        }
    }
}

int main()
{
    get_primes(N-1);
    while(cin>>S)
    {
        len = 0;
        dfs(-1,1,S);

        //sort(res,res+len);//不需要排序

        cout << len << endl;
        if (len)
        {
            sort(res, res + len);//按顺序排序输出
            for (int i = 0; i < len; i ++ ) cout << res[i] << ' ';
            cout << endl;
        }
    }
    return 0;
}

正则问题

1225. 正则问题 - AcWing题库

如果不看视频:真滴难想

第一感觉意味是:栈(逆波兰表达式求值)

看下图和代码注释把^ ^

 

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int k;
string str;

int dfs()
{
    int res = 0;
    while (k < str.size())
    {
        if (str[k] == '(')  // 处理 (......)
        {
            k ++ ;  // 跳过 '('
            res += dfs();//答案+=左括号到其他(‘|’ 或 ‘)’ 或 ‘(’)之前x的数量
            k ++ ; // 跳过 ')'
        }
        else if (str[k] == '|')
        {
            k ++ ;  // 跳过 '|'
            res = max(res, dfs());//左边和右边取最大值
        }
        else if (str[k] == ')') break;
        else
        {
            k ++ ;  // 跳过 'x'
            res ++ ;//计算x的个数
        }
    }

    return res;
}

int main()
{
    cin >> str;
    cout << dfs() << endl;

    return 0;
}

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值