n=a+b/c ==>cn=ca+b,先枚举a,在dfs_a()的叶子结点调用dfs_c来枚举c,而后确定b的值只有一种可能,否则比如b有3个数没选则有3!即6种排列,而通过这一种控制变量推断法可以降低枚举次数来降低时间复杂度
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20;
int n;
bool st[N],backup[N];//记录该位置的数字有没有被使用,注意用bool节省空间
int ans=0;
bool check_b(int a,int c)//判断b中有没有与a、c中重合的元素
{
int b=n*c-a*c;
//各数不能为0
if(!a||!b||!c)return false;
//把b的每一位扣出来与st数组对比,有则不行,无则ans++
memcpy(backup,st,sizeof st);
while(b)
{
//最好一位一位看,且每次只看最低位,这样方便代码编写
int x=b%10;
if(backup[x]||!x)return false;//注意还要求x必须不为0,并且最好用backup数组,因为后面b中有该数需要标记。
backup[x]=true;
b/=10;
}
//最后还要验证一下是不是每个数都被选中过,选中则为true否则false
for(int i=1;i<=9;i++)
if(!backup[i])return false;
return true;
}
void dfs_c(int u,int a,int c)
{
if(u==n)return;//说明n个数字均已经被使用
if(check_b(a,c)) ans++;//这时可以判断有答案了
for(int i=1;i<=9;i++)//由于本题特殊,最好是从1~9,别把0带进去了
{
if(!st[i])//1~9中该数未使用则后加到数位中
{
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);//想想为什么对c的递归写在这里,加个if(a)有什么好处
for(int i=1;i<=9;i++)
{
if(!st[i])//1~9中该数未使用则后加到数位中
{
st[i]=true;
dfs_a(u+1,a*10+i);
st[i]=false;//想想为什么要恢复现场
}
}
}
int main()
{
cin>>n;
dfs_a(0,0);
cout<<ans;
return 0;
}