试题 历届试题 带分数 dfs+剪枝/全排列
问题描述
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
输入格式
从标准输入读入一个正整数N (N<1000*1000)
输出格式
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
样例输入1
100
样例输出1
11
样例输入2
105
样例输出2
6
解题思路:
解题思路:
思路1:用dfs暴力搜索,加上适当条件剪枝以降低时间复杂度。可以看到带分数的形式:
n = a + b/c
首先有 a<n;b/c应为正整数,所以b%c==0且b>c(b数字长度大于等于c)。
利用上述条件作为剪枝条件。
实现时需要先计算n的长度,以此确定a的最大长度(其实对于a用大于等于n长度的都可以,因为最后a>=n的会被剪枝)和b的最小长度(大于等于c的长度)
另外用count()函数计算num[]某区间按顺序组成的数字,注意区间是左闭右开。[ )
实现代码:
#include<cstdio> const int Length = 9;//1-9 //输入 int n; int num[Length]; //保存数字1-9 bool flag[Length+1]; //数字i是否被使用 int res = 0; //保存带分数表示法数目 int length; //输入n的位数(长度) //计算num[]从[start,end)的元素排列 int count(int start,int end) { int s = 0; for( int i=start; i<end; i++ ) { s = s*10 + num[i]; } return s; } //计算某一排列下带分数的个数 void rec() { for( int i=1; i<=length; i++ ) { int a = count(0,i); //num[k](0<=k<i)的下标元素排列 if( a>=n ) return;//剪枝 for( int j = i+(length-i)/2+1; j<Length; j++ ) { // j-i >= (Length-i)/2 因为b的长度>=c的长度 但因为count()是右开,所以再+1 // j<Length 最后给c至少留一位(要注意count()是右开 很容易出错) int b = count(i,j); //num[k](i<=k<j)的下标元素排列 int c = count(j,Length); ////num[k](j<=k<Length)的下标元素排列
if( b>c && b%c==0 && n==a+b/c ){ res++; } } } } //dfs找到所有排列 void dfs(int k) { //找到了某种全排列 if( k==Length ){ rec(); return; } //找全排列 for( int i=1; i<=Length; i++ ) { if( flag[i] ) continue; //数字i已经在num[]里了 跳过这次循环 flag[i] = true; num[k] = i; dfs( k+1 ); flag[i] = false; //回溯 num[]元素可以直接覆盖 所以不用回溯 } } void solve() { //计算n的位数 int n_ = n; while( n_ ) { length++; n_ /= 10; } dfs( 0 ); //num[]从下标0开始找全排列 printf("%d\n",res); } int main() { scanf("%d",&n); solve(); return 0; }
思路2:与思路1大体相同,唯一不同的是其num[] 1-9的全排列是用C++全排列函数next_permutation(int *begin,int *end) 参考https://www.cnblogs.com/cstdio1/p/11311500.html
在<algorithm>下,返回bool类型值,当没有下一排列时返回false,参数为需要排列的地址(左闭右开),其next表示字典序从小到大(对应有prev_permutation())
注意:用next_permutation()求全排列时数组初始为升序排列。
#include<cstdio> #include<algorithm> using namespace std; const int Length = 9;//一位数1-9 //输入 int n; int length;//n位数 int num[Length]; int ans; //计算符合条件的数目 //计算num[]从[start,end)元素和 int count(int start,int end) { int s = 0; for( int i=start; i<end; i++ ) { s = s*10 + num[i]; } return s; } void solve() { //初始化数字数组及输入n位数 for( int i=0; i<Length; i++){ num[i] = i+1; } int n_ = n; while( n_ ) { length++; n_ /= 10; } do { for( int i=1; i<=length; i++ )// x最大为length位数 { int a = count(0,i);//遍历length位数的所有数字 if( a<n ) {//剪枝 for( int j=i+(length-i)/2+1; j<Length; j++ )// j-i>=(length-i)/2 { int b = count(i,j), c = count(j,Length); if( b>c && b%c==0 && n==a+b/c ) { ans++; } } } } }while( next_permutation(num,num+Length));//num的全排列 printf("%d\n",ans); } int main() { scanf("%d",&n); solve(); return 0; }