递归的一些问题

本文介绍了AcWing题库中的几道编程题目,涉及正则表达式的计算、分数转换与组合、带分数全排列优化、有序分数生成、Stern-BrocotTree算法以及分形坐标变换,展示了递归和矩阵操作在解决这些问题中的应用。
摘要由CSDN通过智能技术生成

题目1:正则问题

1225. 正则问题 - AcWing题库

首先要掌握两点正则运算的基础知识:①括号代表优先计算,②或符号代表二选一。

首先计算一下样例:

可以发现,我们的计算过程可以构成一棵树:

当我们碰到左括号时,需要向下走一层,先计算下一层的值。

当我们碰到右括号时,代表当前层计算结束,可以返回上一层。

当我们碰到x,代表当前计算的x总数可以加一。

当我们碰到或符号,则取或符号之前长度和或符号之后长度(从或符号到下一个右括号或者终点)的最大值。

#include<iostream>
#include<cstring>
using namespace std;
string str;

int k=0;

int dfs()
{
    int res=0;
    while(k<str.size())
    {
        if(str[k]=='(')
        {
            k++;
            res+=dfs();
            k++;
        }
        else if(str[k]==')') break;
        else if(str[k]=='x')
        {
            res++;
            k++;
        }
        else if(str[k]=='|')
        {
            k++;
            res=max(res,dfs());
        }
    }
    return res;
}

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

题目2:带分数

1209. 带分数 - AcWing题库

最暴力的做法是,我们枚举1~9的全排列,然后枚举两个断开的地方,将全排列分成三段,分别是a、b、c。这样的时间复杂度是O(9!*9*C_{8}^{2}\textrm{})。

#include<iostream>
using namespace std;
int n;
int cnt=0;
bool st[10];
int a[10];
void check()
{
    int zheng=0;
    for(int i=1;i<=7;i++)
    {
        zheng=zheng*10+a[i];
        if(zheng>=n) break;
        int son=0;
        for(int j=i+1;j<=8;j++)
        {
            son=son*10+a[j];
            int mother=0;
            for(int k=j+1;k<=9;k++) mother=mother*10+a[k];
            if(son%mother) continue;
            if(zheng+son/mother==n) cnt++;
        }
    }
}
void dfs(int u)
{
    if(u>9) 
    {
        check();
        return;
    }
    for(int i=1;i<=9;i++)
    {
        if(st[i]) continue;
        st[i]=1;
        a[u]=i;
        dfs(u+1);
        st[i]=0;
    }
}
int main()
{
    scanf("%d",&n);
    dfs(1);
    cout<<cnt;
}

在此基础上进行优化。我们首先将n=a+b/c转变为cn-ac=b,这样一来,我们只需要进行a、c的全排列,判断b是否合法。这是一个嵌套的bfs。

#include<iostream>
using namespace std;
int n;
int cnt=0;
bool st[10];
typedef long long ll;
bool check(int a,int b,int c)
{
    if(!a||!b||!c) return false;
    bool temp[10];
    for(int i=0;i<=9;i++) temp[i]=st[i];
    while(b)
    {
        int x=b%10;
        if(x==0||temp[x]) return false;
        temp[x]=1;
        b/=10;
    }
    for(int i=1;i<=9;i++) if(temp[i]==0) return false;
    return true;
}
void dfsc(int u,int a,int c)
{
    if(u>9) return;
    int b=(ll)c*n-(ll)a*c;
    if(check(a,b,c)) cnt++;
    for(int i=1;i<=9;i++)
    {
        if(st[i]) continue;
        st[i]=1;
        dfsc(u+1,a,c*10+i);
        st[i]=0;
    }
}
void dfsa(int u,int a)
{
    if(a>=n) return;
    dfsc(u+1,a,0);
    for(int i=1;i<=9;i++)
    {
        if(st[i]) continue;
        st[i]=1;
        dfsa(u+1,a*10+i);
        st[i]=0;
    }
}
int main()
{
    scanf("%d",&n);
    dfsa(1,0);
    cout<<cnt;
}

题目3:有序分数

1360. 有序分数 - AcWing题库

这道题的数据范围很小,可以直接用暴力解法,将所有合法的分数列出来,从大到小排序后输出。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
vector<PII> a;
int n;
int gcd(int a,int b)
{
    if(a%b==0) return b;
    else gcd(b,a%b);
}
bool cmp(PII x,PII y)
{
    return x.x*y.y<x.y*y.x;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(j>=i)
                if(gcd(i,j)==1) 
                    a.push_back({i,j});
        }
    sort(a.begin(),a.end(),cmp);
    for(int i=0;i<a.size();i++) printf("%d/%d\n",a[i].x,a[i].y);
}

这道题的递归解法对应了一个很冷门的知识点——Stern-Brocot Tree。

构建过程如下:

对于a/b和c/d,它的左右子树分别为[a/b,(a+c)/(b+d)]、[(a+c)/(b+d),c/d],左<中<右。

#include<iostream>
int n;
void dfs(int a,int b,int c,int d)
{
    if(b+d>n) return;
    dfs(a,b,a+c,b+d);
    printf("%d/%d\n",a+c,b+d);
    dfs(a+c,b+d,c,d);
}
int main()
{
    scanf("%d",&n);
    printf("%d/%d\n",0,1);
    dfs(0,1,1,1);
    printf("%d/%d\n",1,1);
}

题目4:约数之和

活动 - AcWing

我们对问题进行一些分析:

通过上述分析,我们就得到了递归的雏形。

ac代码如下:

#include<iostream>
using namespace std;
int a,b;
const int mod=9901;
int qmi(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res*=a,res%=mod;
        b=b>>1;
        a*=a%mod;
        a%=mod;
    }
    return res%mod;
}
int sum(int p,int k)
{
    if(k==0) return 1;
    if(k%2) return (1+qmi(p,(k+1)/2))*sum(p,(k-1)/2)%mod;
    else return 1+p%mod*sum(p,k-1)%mod;
}
int main()
{
    scanf("%d%d",&a,&b);
    int res=1;
    if(!a)
    {
        printf("0");
        return 0;
    }
    for(int i=2;i<=a;i++)
    {
        int s=0;
        while(a%i==0)
        {
            s++;
            a/=i;
        }
        res*=sum(i,s*b)%mod;
        res%=mod;
    }
    printf("%d",res);
}

题目5:分形之城

活动 - AcWing

首先观察图形,可以把图形分成四个象限。

先来看等级1如何变化到等级2。

若将等级1放入等级2的第一象限,需要将等级1顺时针旋转90度,并以y轴为轴进行翻转;

若放入第二象限,则不用进行变换;

若放入第三象限,则不用进行变换;

若放入第四象限,需要将等级1逆时针旋转90度,并以y轴为轴进行翻转。

这里需要注意,把等级1放入到等级2时,坐标原点变换了,所以还需要做一个平移的操作。

等级2变换到等级3也遵循这样的规律。

到这里,我们就已经掌握了分形之城的分形规律了。

然后需要推导出坐标变换的规律:

ac代码如下:

#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
#define x first
#define y second
int T;
PLL get_pos(ll n,ll p)
{
    if(n==0) return {0,0};
    ll len=1ll<<(n-1);
    ll cnt=1ll<<(2*n-2);
    PLL pos=get_pos(n-1,p%cnt);
    ll x=pos.x,y=pos.y;
    int z=p/cnt;
    if(z==0) return {-y-len,-x+len};
    else if(z==1) return {x+len,y+len};
    else if(z==2) return {x+len,y-len};
    else return {y-len,x-len};
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int n;
        ll a,b;
        scanf("%d%lld%lld",&n,&a,&b);
        PLL posa=get_pos(n,a-1);
        PLL posb=get_pos(n,b-1);
        double dx=posa.x-posb.x,dy=posa.y-posb.y;
        dx*=5,dy*=5;
        printf("%.0lf\n",sqrt(dx*dx+dy*dy));
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值