带分数(acwing题)

博主分享了一种优化算法,通过将带分数问题中的三个变量转换为两个,仅枚举a和c,利用公式简化判断b。通过DFS搜索和嵌套,减少不必要的枚举,适用于初学者理解。关键步骤包括公式的转化、枚举a和c,以及检查a,b,c是否满足条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

带分数(y总字幕版本,思路贼清晰)

y总的核心思想是:我们如果用暴力解法的话,n是用不到的,只能用于最后的判断,所以这个时候我们考虑把两边同时乘上c,把乘法转换为除法,
然后等式就成了 nc=ca+b,那么我们就可以用上n了,(当然了这个不是重点),而且我们通过观察这个式子,我们本来需要枚举a和b和c,
但是我们现在只需要枚举一下a和c就可以了,因为这样子的话b可以通过 b=cn-ca 直接把b算出来,就不需要枚举b了,这样子的话可以优化很多
当然了,光有这个思路还是远远不够的,这才只是一个思路,但是你想把这个转换成代码还是很有难度的,特别是对于我这种初学者来说,所以
我们还需要分析一下如何写代码。所以我们现在枚举的方式就变成了先枚举a,然后对于每一个枚举的a来说,我们再去枚举c,那么对于每一个枚举
的c来说,a和c已经确定了,那么b也就确定了,就可以进行一个check的判断。虽然这里的话是做了一个很大的一个优化,但是代码写起来还是比较
复杂的。我们先写一个dfs_a函数,先dfs一下a,它其实是一个排列对吧,枚举一个排列,1,2,3,4…然后在dfs_a的内部,在每一个叶子节点的时
候,每一个叶子节点的地方,(因为每一个叶子就代表每一个a的可能成立的方案),那么在a的每一个叶子节点的地方,我们都需要再枚举一下c,
也就是我们要把a的每一个节点扩展成一个搜索树(即在a的每一个叶节点的时候dfs一下c),叶子上的每一棵树都是对c的搜索,因此我们这里其实
是dfs的一个嵌套关系,(即在一个dfs的过程中,把它的叶节点再dfs一下) 所以我们需要在dfs搜索a的时候在a的叶节点的基础上搜索一下对应的
c是多少,那搜完c之后的话我们再去判断一下b就可以了,也就是在c的每一个叶子节点上判断一下b就可以了。所以整个树的话会比较复杂一些。

开一个判重数组,首先dfs一下a,第一个参数表示我们当前已经用了多少个数字,第二个参数表示a是多少,然后直接输出答案

然后写一下dfs_a ,首先判断一下,如果 a>=n 了,那么就不满足条件了对吧,那就直接return了。如果a是可行的,那么我们就dfs一下c

对于dfs_a,然后我们来枚举一下当前这个位置能用哪几个数字,从1到9进行循环,如果说当前这个位置上的数没有被用过的话,那么我们就用它,
并做上标记,然后我们再dfs到下一层,同时更新一下a,传到下一层的参数应该是第一个参数+1,然后a应该变成 a*10+i 。然后最后要记得恢复现
场,因为这里存在一个回溯。

这个是dfs_a,那么我们现在实现一下 dfs_c ,dfs_c的话有三个参数,dfs_c的第一个参数和dfs_a的参数一样,表示我们当前已经用了多少个数,
第二参数的话是a的值,因为我们要利用a的值,第三个参数代表当前c的值。

如果说第一个参数已经等于n了,那就说明我们用完n个数字了对吧,那我们就可以直接return了。否则的话那我们就可以check一下,如果说check
成功的话,那我们的答案就++。(check函数的话就是用来判断一下a和c是否满足要求,传的第一个参数为a,第二个参数为b)否则的话我们就从
1到9枚举一下c,如果这个数没被用过的话,那我们就把它标记一下,并把它放在c的后面,再dfs下一层,下一层的参数就是第一个参数+1,然后
第二个参数不变,第三个参数就是 c*10+i ,然后我们还是要恢复现场。

然后最后再实现一下这个check函数,函数的第一个参数就是a,第二个参数就是c,我们来判断一下a和c是不是满足要求的。那判断a和c是否满足
要求我们需要先利用 b=cn-ca 把计算出来对吧,然后我们再对b的每一位数字分析,第一点看一下它有没有和a和c有相同的数字冲突,第二点
看一下a,b,c是否已经把9个数字全部用完了,那这里的话我们可以搞一个判重数组,我们先把原来的那个数组copy过来,因为我们要对这个判重
数字进行修改,但是我们原来的判重数组是需要保证其是原样的,因为它需要恢复现场(回溯),所以这就是我们多开一个判重数组的原因。然后
我们来取出b的每一位进行判断。在判断的过程中,如果b的某位是0或者b的某位已经出现过了,那就说明出错了,那就直接return false。否则的
话我们给它标记一下它已经用过了,然后我们最后再去遍历一下数组中从1到9的每个数字是否已经被用了(这里不需要判断是否存在多用的情况,
因为不可能存在,因为我们每次都会标记,只有没被用过的数字才会被使用,如果已经被用过的数字将不会被使用),如果判断过程中存在没有被
用过的数字,那说明不满足条件,直接return false。如果上述情况都满足,那就最后return true。当然了这个里面的话我们可以提前判断一些
边界,这个题目中a和b和c都不能为0对吧,所以我们可以在check的一开始判断一下,a或b或c中是否存在0,如果存在,直接return false,这样就
可以做到一个小小的优化以及边界的判定。

总的来说就是我们先枚举一下a,然后对于每个a枚举一下c,然后对于每个a和c我们去判断一下a,b,c是否满足条件就可以了

1、通过公式的转化,将枚举三个变量转换成枚举两个变量(因为第三个变量可以计算出来)

2、枚举 a

3、对于每个a,枚举c

4、对于每个枚举的a和c来说,我们来判断一下b是否成立就可以了

下面是AC代码:

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

using namespace std;

const int N=520;

int had_use[N],ever[N];

int ans=0;

int n;

bool check(int a,int c)
{


    int b=n*c-a*c;//把公式整理一下,然后先把b计算出来

    if(!a||!b||!c)return false;

    memcpy(ever,had_use,sizeof had_use);//因为我们要对这个判断是否出现的数组进行修改,但是原数组又不能变化,所以我们
    //额外开一个数组进行使用,这样就可以达到判断且不会改变原数组的目的
    while(b)
    {
        int t=b%10;//取它的每一位,用来更新一下用过的数字
        b/=10;//删掉这个已经被选中的数
        if(!t||ever[t])return false;
        ever[t]=1;
    }
    for(int i=1;i<=9;i++)//遍历一下,判断每个数
      if(!ever[i])
      return false;

   return true;
}

void dfs_c(int x,int a,int c)//x表示我们已经用了多少个数字
{
    if(x>=10)return;//如果我们把10个数字都用了的话,那就直接return了

    if(check(a,c))ans++;//如果满足要求,那我们判断一下a,c是否符合题目要求,如果符合,那么答案++

    for(int i=1;i<=9;i++)//否则的话我们把c从1到9全部枚举一遍
      if(!had_use[i])
      {
          had_use[i]=1;
          dfs_c(x+1,a,c*10+i);//如果这个数没用过,那么我们就把它放在c的后面,继续dfs下一层
          had_use[i]=0;
      }
}
void dfs_a(int x,int a)
{
    if(a>=n)return;
    if(a)dfs_c(x,a,0);//如果说a是满足情况的,那么我们就枚举一下c,后面那个0表示c的大小

    for(int i=1;i<=9;i++)//枚举一下当前这个位置可以用哪些数字
      if(!had_use[i])
      {
          had_use[i]=1;
          dfs_a(x+1,a*10+i); //如果这个数没有被用过,那么我们就加上它,并且dfs下一层
          had_use[i]=0;//恢复现场,回溯一下
      }

}
int main()
{
    scanf("%d",&n);
    dfs_a(0,0);//第一个0表示我们已经用了多少个数字,后面那个0表示我们当前的a是多少
    printf("%d",ans);    return 0;
}


这个题解是从acwing上面搬过来的,也是自己写的~~

觉得acwing上看的人挺多的就搬过来分享给大家了~~

好了,每篇博客后面都附上一句话:

我相信我的等待会有结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值