1209. 带分数

在这里插入图片描述
大致的思路:
在这里插入图片描述
第一种: 比较暴力的思路就是弄出全排列。
然后再依次的枚举出 a,b,c。最后判断符不符合条件。

第二种就是 既然已经知道n了。那么把c乘过来。这样的话直接枚举 a和c 剩下的b可以直接求得。
最后再检查一下b满足不满足条件。

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

using namespace std;

int n;
int ans=0;
bool st[20],backup[20];

bool check(int a,int c)//检查b是不是满足条件 
{
	long long b=n*(long long)c-a*c;
	if(!a||!b||!c) return false;
	memcpy(backup,st,sizeof(st));
	while(b)
	{
		int x=b%10;
		b/=10;
		if(!x||backup[x]) return false;
		//如果b的数中有0,或者b中的数已经使用过。 
			backup[x]=true;
	}
	for(int i=1;i<=9;i++)//看9个数是不是都用了 
	{
		if(!backup[i])
			return false;
	}
	return true;
}

void dfs_c(int u,int a,int c)//已经用了多少个数字  a是多少 b是多少 
{
	if(u>9) return;
	if(check(a,c)) ans++;
	for(int i=1;i<=9;i++)
	{
		if(!st[i])
		{
			st[i]=true;
			dfs_c(u+1,a,c*10+i);
			st[i]=false;
		}
	}
} 

void dfs_a(int u,int a)
{
	if(a>=n) return;
	if(a) dfs_c(u,a,0);
	for(int i=1;i<=9;i++)
	{
		if(!st[i])
		{
			st[i]=true;
			dfs_a(u+1,a*10+i);
			st[i]=false;
		}
	}
}
int main(void)
{
	cin>>n;
	dfs_a(0,0);
	//已经用了多少个数字
	//a的值是多少。 
	cout<<ans<<endl;
	return 0;
} 

评论区大神的思路:
在这里插入图片描述
关键解析:
我们还需要分析一下如何写代码。所以我们现在枚举的方式就变成了先枚举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就可以了。所以整个树的话会比较复杂一些。
代码:

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

using namespace std;

const int N=10;

int had_use[N],ever[N];

int ans=0;

int n;

bool check(int a,int c)
{


    long long int b=n*(long long)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;
}

原文章链接: https://www.acwing.com/solution/content/38879/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值